<!DOCTYPE html>
<html lang="en-US" dir="ltr">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>安装、启动、进入 | VitePress</title>
    <meta name="description" content="A VitePress site">
    <link rel="preload stylesheet" href="/notebook/assets/style.3dbfd0c2.css" as="style">
    
    <script type="module" src="/notebook/assets/app.8aaa4cbe.js"></script>
    <link rel="preload" href="/notebook/assets/inter-roman-latin.2ed14f66.woff2" as="font" type="font/woff2" crossorigin="">
    <link rel="modulepreload" href="/notebook/assets/chunks/framework.1336c4e5.js">
    <link rel="modulepreload" href="/notebook/assets/chunks/theme.20cddc0c.js">
    <link rel="modulepreload" href="/notebook/assets/2、数据库_Redis_Redis基础.md.856df0e0.lean.js">
    <script id="check-dark-light">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
  </head>
  <body>
    <div id="app"><div class="Layout" data-v-255ec12d><!--[--><!--]--><!--[--><span tabindex="-1" data-v-ae3e3f51></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-ae3e3f51> Skip to content </a><!--]--><!----><header class="VPNav" data-v-255ec12d data-v-7e5bc4a5><div class="VPNavBar has-sidebar" data-v-7e5bc4a5 data-v-0937f67c><div class="container" data-v-0937f67c><div class="title" data-v-0937f67c><div class="VPNavBarTitle has-sidebar" data-v-0937f67c data-v-86d1bed8><a class="title" href="/notebook/" data-v-86d1bed8><!--[--><!--]--><!--[--><img class="VPImage logo" src="/notebook/Vue.png" alt data-v-8426fc1a><!--]--><!--[-->任硕的文档<!--]--><!--[--><!--]--></a></div></div><div class="content" data-v-0937f67c><div class="curtain" data-v-0937f67c></div><div class="content-body" data-v-0937f67c><!--[--><!--]--><div class="VPNavBarSearch search" style="--vp-meta-key:&#39;Meta&#39;;" data-v-0937f67c><!--[--><!----><div id="docsearch"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><svg class="DocSearch-Search-Icon" width="20" height="20" viewBox="0 0 20 20" aria-label="search icon"><path d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z" stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"></path></svg><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"><kbd class="DocSearch-Button-Key"></kbd><kbd class="DocSearch-Button-Key">K</kbd></span></button></div><!--]--></div><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-0937f67c data-v-7f418b0f><span id="main-nav-aria-label" class="visually-hidden" data-v-7f418b0f>Main Navigation</span><!--[--><!--[--><div class="VPFlyout VPNavBarMenuGroup" data-v-7f418b0f data-v-a7b5672a><button type="button" class="button" aria-haspopup="true" aria-expanded="false" data-v-a7b5672a><span class="text" data-v-a7b5672a><!----><span data-v-a7b5672a>Java学前端</span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="text-icon" data-v-a7b5672a><path d="M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"></path></svg></span></button><div class="menu" data-v-a7b5672a><div class="VPMenu" data-v-a7b5672a data-v-e7ea1737><div class="items" data-v-e7ea1737><!--[--><!--[--><div class="VPMenuGroup" data-v-e7ea1737 data-v-69e747b5><!----><!--[--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/Java%E5%AD%A6%E5%89%8D%E7%AB%AF/HTML+JS.html" data-v-2f2cfafc><!--[-->HTML+JS<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/Java%E5%AD%A6%E5%89%8D%E7%AB%AF/CSS.html" data-v-2f2cfafc><!--[-->CSS<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/Java%E5%AD%A6%E5%89%8D%E7%AB%AF/Vue2+%E7%BB%84%E4%BB%B6.html" data-v-2f2cfafc><!--[-->Vue2+组件<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/Java%E5%AD%A6%E5%89%8D%E7%AB%AF/Vue3+%E7%BB%84%E4%BB%B6.html" data-v-2f2cfafc><!--[-->Vue3+组件<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/Java%E5%AD%A6%E5%89%8D%E7%AB%AF/React.html" data-v-2f2cfafc><!--[-->React<!--]--></a></div><!--]--><!--]--></div><!--]--><!--]--></div><!--[--><!--]--></div></div></div><!--]--><!--[--><div class="VPFlyout VPNavBarMenuGroup" data-v-7f418b0f data-v-a7b5672a><button type="button" class="button" aria-haspopup="true" aria-expanded="false" data-v-a7b5672a><span class="text" data-v-a7b5672a><!----><span data-v-a7b5672a>软件测试</span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="text-icon" data-v-a7b5672a><path d="M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"></path></svg></span></button><div class="menu" data-v-a7b5672a><div class="VPMenu" data-v-a7b5672a data-v-e7ea1737><div class="items" data-v-e7ea1737><!--[--><!--[--><div class="VPMenuGroup" data-v-e7ea1737 data-v-69e747b5><!----><!--[--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/%E8%BD%AF%E4%BB%B6%E6%B5%8B%E8%AF%95/%E6%B5%8B%E8%AF%95%E5%9F%BA%E7%A1%80.html" data-v-2f2cfafc><!--[-->测试基础<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/%E8%BD%AF%E4%BB%B6%E6%B5%8B%E8%AF%95/%E5%8E%8B%E5%8A%9B%E6%B5%8B%E8%AF%95.html" data-v-2f2cfafc><!--[-->压力测试<!--]--></a></div><!--]--><!--]--></div><!--]--><!--]--></div><!--[--><!--]--></div></div></div><!--]--><!--[--><div class="VPFlyout VPNavBarMenuGroup" data-v-7f418b0f data-v-a7b5672a><button type="button" class="button" aria-haspopup="true" aria-expanded="false" data-v-a7b5672a><span class="text" data-v-a7b5672a><!----><span data-v-a7b5672a>多线程</span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="text-icon" data-v-a7b5672a><path d="M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"></path></svg></span></button><div class="menu" data-v-a7b5672a><div class="VPMenu" data-v-a7b5672a data-v-e7ea1737><div class="items" data-v-e7ea1737><!--[--><!--[--><div class="VPMenuGroup" data-v-e7ea1737 data-v-69e747b5><!----><!--[--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/%E5%B9%B6%E5%8F%91%20&amp;%20%E5%A4%9A%E7%BA%BF%E7%A8%8B/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-2f2cfafc><!--[-->基础篇<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/%E5%B9%B6%E5%8F%91%20&amp;%20%E5%A4%9A%E7%BA%BF%E7%A8%8B/%E5%B9%B6%E5%8F%91%E5%AE%8C%E5%96%84.html" data-v-2f2cfafc><!--[-->进阶篇<!--]--></a></div><!--]--><!--]--></div><!--]--><!--]--></div><!--[--><!--]--></div></div></div><!--]--><!--[--><div class="VPFlyout VPNavBarMenuGroup" data-v-7f418b0f data-v-a7b5672a><button type="button" class="button" aria-haspopup="true" aria-expanded="false" data-v-a7b5672a><span class="text" data-v-a7b5672a><!----><span data-v-a7b5672a>开发工具</span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="text-icon" data-v-a7b5672a><path d="M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"></path></svg></span></button><div class="menu" data-v-a7b5672a><div class="VPMenu" data-v-a7b5672a data-v-e7ea1737><div class="items" data-v-e7ea1737><!--[--><!--[--><div class="VPMenuGroup" data-v-e7ea1737 data-v-69e747b5><!----><!--[--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/IDEA/Chrome.html" data-v-2f2cfafc><!--[-->Chrome<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/IDEA/IDEA%E5%9F%BA%E7%A1%80.html" data-v-2f2cfafc><!--[-->IDEA基础<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/IDEA/IDEA%E6%8F%92%E4%BB%B6.html" data-v-2f2cfafc><!--[-->IDEA插件<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/IDEA/VS%20Code.html" data-v-2f2cfafc><!--[-->VS Code<!--]--></a></div><!--]--><!--]--></div><!--]--><!--]--></div><!--[--><!--]--></div></div></div><!--]--><!--[--><div class="VPFlyout VPNavBarMenuGroup" data-v-7f418b0f data-v-a7b5672a><button type="button" class="button" aria-haspopup="true" aria-expanded="false" data-v-a7b5672a><span class="text" data-v-a7b5672a><!----><span data-v-a7b5672a>消息中间件</span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="text-icon" data-v-a7b5672a><path d="M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"></path></svg></span></button><div class="menu" data-v-a7b5672a><div class="VPMenu" data-v-a7b5672a data-v-e7ea1737><div class="items" data-v-e7ea1737><!--[--><!--[--><div class="VPMenuGroup" data-v-e7ea1737 data-v-69e747b5><!----><!--[--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/RabbitMQ.html" data-v-2f2cfafc><!--[-->RabbitMQ<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/RocketMQ.html" data-v-2f2cfafc><!--[-->RocketMQ<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/Kafka.html" data-v-2f2cfafc><!--[-->Kafka<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/Canal.html" data-v-2f2cfafc><!--[-->Canal<!--]--></a></div><!--]--><!--]--></div><!--]--><!--]--></div><!--[--><!--]--></div></div></div><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-0937f67c data-v-f6a63727><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title="toggle dark mode" aria-checked="false" data-v-f6a63727 data-v-82b282f1 data-v-f3c41672><span class="check" data-v-f3c41672><span class="icon" data-v-f3c41672><!--[--><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="sun" data-v-82b282f1><path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z"></path><path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z"></path><path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z"></path><path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z"></path><path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z"></path><path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z"></path><path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z"></path><path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z"></path><path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="moon" data-v-82b282f1><path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"></path></svg><!--]--></span></span></button></div><div class="VPSocialLinks VPNavBarSocialLinks social-links" data-v-0937f67c data-v-0394ad82 data-v-7bc22406><!--[--><a class="VPSocialLink no-icon" href="https://github.com/renshuo123/renshuo123.github.io" aria-label="github" target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></a><a class="VPSocialLink no-icon" href="#" aria-label="twitter" target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Twitter</title><path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg></a><a class="VPSocialLink no-icon" href="https://github.com/" aria-label target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg t="1676028692954" class="icon" ...</path></svg></a><!--]--></div><div class="VPFlyout VPNavBarExtra extra" data-v-0937f67c data-v-40855f84 data-v-a7b5672a><button type="button" class="button" aria-haspopup="true" aria-expanded="false" aria-label="extra navigation" data-v-a7b5672a><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="icon" data-v-a7b5672a><circle cx="12" cy="12" r="2"></circle><circle cx="19" cy="12" r="2"></circle><circle cx="5" cy="12" r="2"></circle></svg></button><div class="menu" data-v-a7b5672a><div class="VPMenu" data-v-a7b5672a data-v-e7ea1737><!----><!--[--><!--[--><!----><div class="group" data-v-40855f84><div class="item appearance" data-v-40855f84><p class="label" data-v-40855f84>Appearance</p><div class="appearance-action" data-v-40855f84><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title="toggle dark mode" aria-checked="false" data-v-40855f84 data-v-82b282f1 data-v-f3c41672><span class="check" data-v-f3c41672><span class="icon" data-v-f3c41672><!--[--><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="sun" data-v-82b282f1><path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z"></path><path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z"></path><path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z"></path><path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z"></path><path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z"></path><path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z"></path><path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z"></path><path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z"></path><path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="moon" data-v-82b282f1><path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"></path></svg><!--]--></span></span></button></div></div></div><div class="group" data-v-40855f84><div class="item social-links" data-v-40855f84><div class="VPSocialLinks social-links-list" data-v-40855f84 data-v-7bc22406><!--[--><a class="VPSocialLink no-icon" href="https://github.com/renshuo123/renshuo123.github.io" aria-label="github" target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></a><a class="VPSocialLink no-icon" href="#" aria-label="twitter" target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Twitter</title><path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg></a><a class="VPSocialLink no-icon" href="https://github.com/" aria-label target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg t="1676028692954" class="icon" ...</path></svg></a><!--]--></div></div></div><!--]--><!--]--></div></div></div><!--[--><!--]--><button type="button" class="VPNavBarHamburger hamburger" aria-label="mobile navigation" aria-expanded="false" aria-controls="VPNavScreen" data-v-0937f67c data-v-e5dd9c1c><span class="container" data-v-e5dd9c1c><span class="top" data-v-e5dd9c1c></span><span class="middle" data-v-e5dd9c1c></span><span class="bottom" data-v-e5dd9c1c></span></span></button></div></div></div></div><!----></header><div class="VPLocalNav reached-top" data-v-255ec12d data-v-5cfd5582><button class="menu" aria-expanded="false" aria-controls="VPSidebarNav" data-v-5cfd5582><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="menu-icon" data-v-5cfd5582><path d="M17,11H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,11,17,11z"></path><path d="M21,7H3C2.4,7,2,6.6,2,6s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,7,21,7z"></path><path d="M21,15H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,15,21,15z"></path><path d="M17,19H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,19,17,19z"></path></svg><span class="menu-text" data-v-5cfd5582>Menu</span></button><div class="VPLocalNavOutlineDropdown" style="--vp-vh:0px;" data-v-5cfd5582 data-v-18201f51><button data-v-18201f51>Return to top</button><!----></div></div><aside class="VPSidebar" data-v-255ec12d data-v-845b8fc6><div class="curtain" data-v-845b8fc6></div><nav class="nav" id="VPSidebarNav" aria-labelledby="sidebar-aria-label" tabindex="-1" data-v-845b8fc6><span class="visually-hidden" id="sidebar-aria-label" data-v-845b8fc6> Sidebar Navigation </span><!--[--><!--]--><!--[--><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>Java</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Java/Java%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Java基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Java/Java%E6%96%B0%E7%89%B9%E6%80%A7.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Java新特性</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Java/Java%E8%BF%9B%E9%98%B6.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Java进阶</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Java/Java%E9%9B%86%E5%90%88.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Java集合</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Java/Java%E9%AB%98%E7%BA%A7.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Java高级</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>Linux</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Linux/Linux%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Linux基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Linux/Linux%E8%BF%9B%E9%98%B6.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Linux新特性</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Linux/Shell.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Shell脚本</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Linux/%E5%AE%9E%E7%94%A8%E8%84%9A%E6%9C%AC.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>实用脚本</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Linux/%E8%BD%AF%E4%BB%B6%E9%83%A8%E7%BD%B2.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>软件部署</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>Nginx</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Nginx/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>基础篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Nginx/%E8%BF%9B%E9%98%B6%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>进阶篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Nginx/%E5%AE%9E%E6%88%98%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>实战篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Nginx/%E9%9D%A2%E8%AF%95%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>面试篇</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>SSM</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/SSM/Maven.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Maven</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/SSM/Spring.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Spring</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/SSM/SpringMVC.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>SpringMVC</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/SSM/SpringBatch.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>SpringBatch</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>SpringBoot</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/3%E3%80%81SpringBoot/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>基础篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/3%E3%80%81SpringBoot/%E5%BA%94%E7%94%A8%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>应用篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/3%E3%80%81SpringBoot/%E6%96%B0%E7%89%B9%E6%80%A7.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>新特性</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/3%E3%80%81SpringBoot/%E8%BF%90%E7%BB%B4&amp;%E5%8E%9F%E7%90%86.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>运维&原理</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>SpringCloud</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/4%E3%80%81%E5%BE%AE%E6%9C%8D%E5%8A%A1/%E8%BF%9B%E9%98%B6.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>SpringCloud</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/4%E3%80%81%E5%BE%AE%E6%9C%8D%E5%8A%A1/%E5%BF%85%E5%A4%87/Sentinel.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Sentinel</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>SpringSecurity</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/4%E3%80%81%E5%BE%AE%E6%9C%8D%E5%8A%A1/SpringSecurity/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>SpringSecurity基础篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/4%E3%80%81%E5%BE%AE%E6%9C%8D%E5%8A%A1/SpringSecurity/%E8%BF%9B%E9%98%B6%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>SpringSecurity进阶篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/4%E3%80%81%E5%BE%AE%E6%9C%8D%E5%8A%A1/SpringSecurity/%E9%AB%98%E7%BA%A7%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>SpringSecurity高级篇</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>Mybatis & MybatisPlus</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Mybatis&amp;MybatisPlus/Mybatis.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Mybatis</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Mybatis&amp;MybatisPlus/MybatisPlus.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>MybatisPlus</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Mybatis&amp;MybatisPlus/JPA.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>JPA</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>Git & ChatGPT</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/5%E3%80%81%E8%BF%90%E7%BB%B4/Git.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Git</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/5%E3%80%81%E8%BF%90%E7%BB%B4/Github.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Github</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/5%E3%80%81%E8%BF%90%E7%BB%B4/ChatGPT.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>ChatGPT</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/5%E3%80%81%E8%BF%90%E7%BB%B4/Jenkins.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Jenkins</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/5%E3%80%81%E8%BF%90%E7%BB%B4/Netty.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Netty</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible has-active" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>数据库</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>MySQL</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/MySQL/MySQL%E6%A0%B8%E5%BF%83/%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>MySQL基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/MySQL/MySQL%E6%A0%B8%E5%BF%83/%E8%BF%9B%E9%98%B6.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>MySQL进阶</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/MySQL/MySQL%E6%A0%B8%E5%BF%83/%E4%BC%98%E5%8C%96.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>MySQL优化</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/MySQL/MySQL%E6%A0%B8%E5%BF%83/%E8%AE%BE%E8%AE%A1.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>MySQL设计</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/MySQL/MySQL%E6%A0%B8%E5%BF%83/%E8%BF%90%E7%BB%B4.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>MySQL运维</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/MySQL/%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>分库分表</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible has-active" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>Redis</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/Redis/Redis%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Redis基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/Redis/Redis%E4%BC%98%E5%8C%96.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Redis优化</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/Redis/Redis%E5%8E%9F%E7%90%86.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Redis原理</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/Redis/Redis%E9%AB%98%E7%BA%A7.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Redis高级</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/Redis/Redis%E5%AE%9E%E6%88%98.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Redis实战</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/Redis/%E6%9C%AC%E5%9C%B0%E7%BC%93%E5%AD%98.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>本地缓存</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>MongoDB</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/MongoDB/%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>MongoDB基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/MongoDB/%E6%95%B4%E5%90%88.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>MongoDB进阶</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>ElasticSearch</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/ElasticSearch/1%E3%80%81ES%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>ES基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/ElasticSearch/3%E3%80%81ES%E9%AB%98%E7%BA%A7.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>ES高级</p><!--]--></a><!----></div><!----></div><!--]--></div></section><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/influxdb.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>InfluxDB</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/Neo4j.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Neo4j</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>高并发 & 秒杀 & 分布式</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E4%B8%89%E9%AB%98/%E5%88%86%E5%B8%83%E5%BC%8F.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>分布式理论</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/4%E3%80%81%E5%BE%AE%E6%9C%8D%E5%8A%A1/%E5%BF%85%E5%A4%87/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>分布式锁</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E4%B8%89%E9%AB%98/%E7%A7%92%E6%9D%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>秒杀</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E4%B8%89%E9%AB%98/%E9%AB%98%E5%8F%AF%E7%94%A8.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>高可用</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E4%B8%89%E9%AB%98/%E9%AB%98%E5%B9%B6%E5%8F%91.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>高并发</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>云原生</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E4%BA%91%E5%8E%9F%E7%94%9F/Docker.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Docker</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E4%BA%91%E5%8E%9F%E7%94%9F/K8S.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>K8S</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>可视化 & 监控</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E5%8F%AF%E8%A7%86%E5%8C%96%20&amp;%20%E7%9B%91%E6%8E%A7/%E7%9B%91%E6%8E%A7%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>监控基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E5%8F%AF%E8%A7%86%E5%8C%96%20&amp;%20%E7%9B%91%E6%8E%A7/%E7%9B%91%E6%8E%A7%E8%BF%9B%E9%98%B6.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>监控进阶</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E5%8F%AF%E8%A7%86%E5%8C%96%20&amp;%20%E7%9B%91%E6%8E%A7/%E5%8F%AF%E8%A7%86%E5%8C%96%E5%A4%A7%E5%B1%8F.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>可视化大屏</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E5%8F%AF%E8%A7%86%E5%8C%96%20&amp;%20%E7%9B%91%E6%8E%A7/Zabbix.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Zabbix</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>学前端</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>HTML+CSS</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/1%E3%80%81HTML+CSS/HTML%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>HTML基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/1%E3%80%81HTML+CSS/CSS%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>CSS基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/1%E3%80%81HTML+CSS/%E7%BD%91%E9%A1%B5%E8%BF%9B%E9%98%B6.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>网页进阶</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>JS+TS</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/2%E3%80%81JS+TS/JS%20%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>JS基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/2%E3%80%81JS+TS/JS%20%E8%BF%9B%E9%98%B6.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>JS进阶</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/2%E3%80%81JS+TS/ES6%20%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>ES6基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/2%E3%80%81JS+TS/ES6%20%E8%BF%9B%E9%98%B6.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>ES6进阶</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/2%E3%80%81JS+TS/TypeScript.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>TS基础</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>NodeJS</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/4%E3%80%81Node/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Node基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/4%E3%80%81Node/%E8%BF%9B%E9%98%B6%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Node进阶</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/4%E3%80%81Node/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>项目实战</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>Vue</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/3%E3%80%81Vue/Vue3/Vue3%E8%BF%9B%E9%98%B6.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Vue3进阶</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/3%E3%80%81Vue/Vue3/Vue3%E9%AB%98%E7%BA%A7.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Vue3高级</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/3%E3%80%81Vue/Vue3/Vue3%E6%96%B0%E8%AF%AD%E6%B3%95.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Vue3新语法</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/3%E3%80%81Vue/Vue2/Vue2%E9%A1%B9%E7%9B%AE.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>项目实战</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>小程序</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/5%E3%80%81%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>小程序基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/5%E3%80%81%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E4%BC%98%E5%8C%96.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>小程序优化</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/5%E3%80%81%E5%B0%8F%E7%A8%8B%E5%BA%8F/uniapp.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>uniapp</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/5%E3%80%81%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E9%A1%B9%E7%9B%AE.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>项目实战</p><!--]--></a><!----></div><!----></div><!--]--></div></section><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>计算机基础</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>数据结构</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>操作系统</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>设计模式</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>计算机网络</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/UML.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>UML</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/%E7%AE%97%E6%B3%95/LeetCode.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>LeetCode</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>项目实战</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>云尚办公</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E4%BA%91%E5%B0%9A%E5%8A%9E%E5%85%AC/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>基础篇</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>小兔鲜</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E5%B0%8F%E5%85%94%E9%B2%9C/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>基础篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E5%B0%8F%E5%85%94%E9%B2%9C/%E8%BF%9B%E9%98%B6%E7%AF%871.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>进阶篇1</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E5%B0%8F%E5%85%94%E9%B2%9C/%E8%BF%9B%E9%98%B6%E7%AF%872.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>进阶篇2</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>地图</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>基础篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE/%E8%BF%9B%E9%98%B6%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>进阶篇</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>苍穹外卖</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E8%8B%8D%E7%A9%B9%E5%A4%96%E5%8D%96/%E8%BF%9B%E9%98%B6%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>进阶篇</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>黑马头条</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E9%BB%91%E9%A9%AC%E5%A4%B4%E6%9D%A1/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>基础篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E9%BB%91%E9%A9%AC%E5%A4%B4%E6%9D%A1/%E8%BF%9B%E9%98%B6%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>进阶篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E9%BB%91%E9%A9%AC%E5%A4%B4%E6%9D%A1/%E8%BF%9B%E9%98%B6%E7%AF%872.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>进阶篇2</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E9%BB%91%E9%A9%AC%E5%A4%B4%E6%9D%A1/%E9%AB%98%E7%BA%A7%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>高级篇</p><!--]--></a><!----></div><!----></div><!--]--></div></section><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E6%94%AF%E4%BB%98.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>支付</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E9%A1%B9%E7%9B%AE%E6%8E%A8%E8%8D%90.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>项目推荐</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0" data-v-845b8fc6 data-v-9b797284><!----><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/team.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>团队成员</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><!--]--><!--[--><!--]--></nav></aside><div class="VPContent has-sidebar" id="VPContent" data-v-255ec12d data-v-669faec9><div class="VPDoc has-sidebar has-aside" data-v-669faec9 data-v-6b87e69f><!--[--><!--]--><div class="container" data-v-6b87e69f><div class="aside" data-v-6b87e69f><div class="aside-curtain" data-v-6b87e69f></div><div class="aside-container" data-v-6b87e69f><div class="aside-content" data-v-6b87e69f><div class="VPDocAside" data-v-6b87e69f data-v-3f215769><!--[--><!--]--><!--[--><!--]--><div class="VPDocAsideOutline" data-v-3f215769 data-v-ff0f39c8><div class="content" data-v-ff0f39c8><div class="outline-marker" data-v-ff0f39c8></div><div class="outline-title" data-v-ff0f39c8>On this page</div><nav aria-labelledby="doc-outline-aria-label" data-v-ff0f39c8><span class="visually-hidden" id="doc-outline-aria-label" data-v-ff0f39c8> Table of Contents for current page </span><ul class="root" data-v-ff0f39c8 data-v-d0ee3533><!--[--><!--]--></ul></nav></div></div><!--[--><!--]--><div class="spacer" data-v-3f215769></div><!--[--><!--]--><!----><!--[--><!--]--><!--[--><!--]--></div></div></div></div><div class="content" data-v-6b87e69f><div class="content-container" data-v-6b87e69f><!--[--><!--]--><!----><main class="main" data-v-6b87e69f><div style="position:relative;" class="vp-doc _notebook_2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93_Redis_Redis%E5%9F%BA%E7%A1%80" data-v-6b87e69f><div><h1 id="安装、启动、进入" tabindex="-1">安装、启动、进入 <a class="header-anchor" href="#安装、启动、进入" aria-label="Permalink to &quot;安装、启动、进入&quot;">​</a></h1><p><a href="https://mp.weixin.qq.com/s?__biz=Mzg5NDY3NzIwMA==&amp;mid=2247505344&amp;idx=1&amp;sn=6868afa74c816ccd0334055c899e4081&amp;chksm=c0196c7ff76ee56909c890542a27c07f4895e14ecc6b1bc10fbabe47c5fad242fb3d8de198fa&amp;mpshare=1&amp;scene=23&amp;srcid=0416yXB6nHnmAIdI3e2EKnpv&amp;sharer_sharetime=1681660511765&amp;sharer_shareid=29b8a04db1dbd975e3bf4e9f47e7ac67#rd" target="_blank" rel="noreferrer">史上最全系列 | Redis 原理+知识点总结（1.5万字、8大知识点、17张图）</a></p><h2 id="下载和安装" tabindex="-1">下载和安装 <a class="header-anchor" href="#下载和安装" aria-label="Permalink to &quot;下载和安装&quot;">​</a></h2><p>此处选择的Linux版本为CentOS 7，Redis的官方网站地址：<a href="https://redis.io/" target="_blank" rel="noreferrer">https://redis.io/</a></p><p>安装C 语言的编译环境，Redis是基于C语言编写的，因此首先需要安装Redis所需要的gcc依赖：</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">yum</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">install</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-y</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">gcc</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">tcl</span></span>
<span class="line"><span style="color:#FFCB6B;">gcc</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">--version</span></span></code></pre></div><p>下载并解压</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">tar</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-zxvf</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">redis-6.2.6.tar.gz</span></span>
<span class="line"><span style="color:#82AAFF;">cd</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">redis-6.2.6</span></span></code></pre></div><blockquote><p>在redis-6.2.6目录下再次执行make命令（只是编译好），注意：如果没有准备好C语言编译环境，make 会报错—Jemalloc/jemalloc.h：没有那个文件。解决方案：运行make distclean，在该目录依次执行</p></blockquote><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">make</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&amp;&amp;</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">make</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">install</span></span></code></pre></div><h2 id="默认安装目录" tabindex="-1">默认安装目录 <a class="header-anchor" href="#默认安装目录" aria-label="Permalink to &quot;默认安装目录&quot;">​</a></h2><blockquote><p>安装完成后，默认安装在/usr/local/bin</p></blockquote><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#82AAFF;">cd</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">/usr/local/bin</span></span></code></pre></div><p>redis-benchmark:性能测试工具，可以在自己本子运行，看看自己本子性能如何,进入/usr/local/bin,执行,即可执行测试</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">./redis-benchmark</span></span></code></pre></div><blockquote><ul><li><p>redis-check-aof：修复有问题的AOF文件，rdb和aof后面讲</p></li><li><p>redis-check-dump：修复有问题的dump.rdb文件</p></li><li><p>redis-sentinel：Redis集群使用</p></li><li><p>redis-server：Redis服务器启动命令</p></li><li><p>redis-cli：客户端，操作入口</p></li></ul></blockquote><h2 id="启动-redis" tabindex="-1">启动 Redis <a class="header-anchor" href="#启动-redis" aria-label="Permalink to &quot;启动 Redis&quot;">​</a></h2><p>redis的启动方式有很多种，例如：</p><h3 id="默认启动-不推荐" tabindex="-1">默认启动 不推荐 <a class="header-anchor" href="#默认启动-不推荐" aria-label="Permalink to &quot;默认启动 不推荐&quot;">​</a></h3><blockquote><p>安装完成后，在任意目录输入redis-server命令即可启动Redis：</p></blockquote><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">redis</span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">server</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img/image-20211211081716167.png" alt="image-20211211081716167" style="zoom:40%;"><p>这种启动属于<code>前台启动</code>，会阻塞整个会话窗口，窗口关闭或者按下<code>CTRL + C</code>则Redis停止。不推荐使用</p><h3 id="指定配置文件启动-⭐" tabindex="-1">指定配置文件启动 ⭐ <a class="header-anchor" href="#指定配置文件启动-⭐" aria-label="Permalink to &quot;指定配置文件启动 ⭐&quot;">​</a></h3><h4 id="修改配置文件" tabindex="-1">修改配置文件 <a class="header-anchor" href="#修改配置文件" aria-label="Permalink to &quot;修改配置文件&quot;">​</a></h4><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">find</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">/</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-name</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">redis.conf</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 我们先将这个配置文件备份一份</span></span>
<span class="line"><span style="color:#FFCB6B;">cp</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">/etc/redis.conf</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">redis.conf.bck</span></span></code></pre></div><div class="language-properties"><button title="Copy Code" class="copy"></button><span class="lang">properties</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;"># 允许访问的地址，默认是127.0.0.1，会导致只能在本地访问。</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 修改为0.0.0.0则可以在任意IP访问，生产环境不要设置为0.0.0.0</span></span>
<span class="line"><span style="color:#A6ACCD;">bind 0.0.0.0</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 守护进程，修改为yes后即可后台运行</span></span>
<span class="line"><span style="color:#A6ACCD;">daemonize yes </span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 关闭保护模式，不然远程连不上</span></span>
<span class="line"><span style="color:#A6ACCD;">protected-mode no</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 设置密码，连接时需要加上-a参数</span></span>
<span class="line"><span style="color:#A6ACCD;">requirepass 315217</span></span></code></pre></div><h4 id="根据配置文件启动" tabindex="-1">根据配置文件启动 <a class="header-anchor" href="#根据配置文件启动" aria-label="Permalink to &quot;根据配置文件启动&quot;">​</a></h4><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">redis-server</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">/etc/redis.conf</span></span>
<span class="line"><span style="color:#FFCB6B;">redis-cli</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-p</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">6379</span></span></code></pre></div><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;"># 利用redis-cli来执行 shutdown 命令，即可停止 Redis 服务，</span></span>
<span class="line"><span style="color:#FFCB6B;">redis-cli</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">shutdown</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 因为之前配置了密码，因此需要通过 -u 来指定密码</span></span>
<span class="line"><span style="color:#FFCB6B;">redis-cli</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-u</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">123321</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">shutdown</span></span></code></pre></div><h2 id="注册系统服务⭐" tabindex="-1">注册系统服务⭐ <a class="header-anchor" href="#注册系统服务⭐" aria-label="Permalink to &quot;注册系统服务⭐&quot;">​</a></h2><p>我们也可以通过配置来实现开机自启。首先，新建一个系统服务文件：</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">vi</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">/etc/systemd/system/redis.service</span></span></code></pre></div><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">Unit</span><span style="color:#89DDFF;">]</span></span>
<span class="line"><span style="color:#A6ACCD;">Description</span><span style="color:#89DDFF;">=</span><span style="color:#C3E88D;">redis-server</span></span>
<span class="line"><span style="color:#A6ACCD;">After</span><span style="color:#89DDFF;">=</span><span style="color:#C3E88D;">network.target</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">Service</span><span style="color:#89DDFF;">]</span></span>
<span class="line"><span style="color:#A6ACCD;">Type</span><span style="color:#89DDFF;">=</span><span style="color:#C3E88D;">forking</span></span>
<span class="line"><span style="color:#A6ACCD;">ExecStart</span><span style="color:#89DDFF;">=</span><span style="color:#C3E88D;">/usr/local/bin/redis-server</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">/etc/redis.conf</span></span>
<span class="line"><span style="color:#A6ACCD;">PrivateTmp</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">true</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">Install</span><span style="color:#89DDFF;">]</span></span>
<span class="line"><span style="color:#A6ACCD;">WantedBy</span><span style="color:#89DDFF;">=</span><span style="color:#C3E88D;">multi-user.target</span></span></code></pre></div><p>然后重载系统服务：</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">systemctl</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">daemon-reload</span></span></code></pre></div><p>现在，我们可以用下面这组命令来操作redis了：</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;"># 启动</span></span>
<span class="line"><span style="color:#FFCB6B;">systemctl</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">start</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">redis</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 停止</span></span>
<span class="line"><span style="color:#FFCB6B;">systemctl</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">stop</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">redis</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 重启</span></span>
<span class="line"><span style="color:#FFCB6B;">systemctl</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">restart</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">redis</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 查看状态</span></span>
<span class="line"><span style="color:#FFCB6B;">systemctl</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">status</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">redis</span></span></code></pre></div><p>执行下面的命令，可以让redis开机自启：</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">systemctl</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">enable</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">redis</span></span></code></pre></div><h2 id="win10-redis安装" tabindex="-1">Win10 Redis安装 <a class="header-anchor" href="#win10-redis安装" aria-label="Permalink to &quot;Win10 Redis安装&quot;">​</a></h2><p><a href="https://github.com/redis-windows/redis-windows/releases/tag/7.0.8" target="_blank" rel="noreferrer">https://github.com/redis-windows/redis-windows/releases/tag/7.0.8</a></p><p><a href="https://dblab.xmu.edu.cn/blog/4091/" target="_blank" rel="noreferrer">https://dblab.xmu.edu.cn/blog/4091/</a></p><p><a href="https://github.com/redis-windows/redis-windows/blob/main/README.zh_CN.md" target="_blank" rel="noreferrer">https://github.com/redis-windows/redis-windows/blob/main/README.zh_CN.md</a></p><blockquote><p>以管理员运行cmd</p></blockquote><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">sc.exe</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">create</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">Redis</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">binpath=D:</span><span style="color:#A6ACCD;">\R</span><span style="color:#C3E88D;">edis-7.0.11-Windows-x64-with-Service</span><span style="color:#A6ACCD;">\R</span><span style="color:#C3E88D;">edisService.exe</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">start=</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">auto</span></span>
<span class="line"><span style="color:#FFCB6B;">net</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">start</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">Redis</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.6.17/image-20230703162112922.png" alt="image-20230703162112922" style="zoom:80%;"><div class="language-properties"><button title="Copy Code" class="copy"></button><span class="lang">properties</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;"># 允许访问的地址，默认是127.0.0.1，会导致只能在本地访问。</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 修改为0.0.0.0则可以在任意IP访问，生产环境不要设置为0.0.0.0</span></span>
<span class="line"><span style="color:#A6ACCD;">bind 0.0.0.0</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 守护进程，修改为yes后即可后台运行</span></span>
<span class="line"><span style="color:#A6ACCD;">daemonize yes </span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 关闭保护模式，不然远程连不上</span></span>
<span class="line"><span style="color:#A6ACCD;">protected-mode no</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 设置密码，连接时需要加上-a参数</span></span>
<span class="line"><span style="color:#A6ACCD;">requirepass 315210</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.6.17/image-20230703162028816.png" alt="image-20230703162028816" style="zoom:80%;"><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.6.17/image-20230703163720889.png" alt="image-20230703163720889" style="zoom:80%;"><h1 id="redis客户端" tabindex="-1">Redis客户端 <a class="header-anchor" href="#redis客户端" aria-label="Permalink to &quot;Redis客户端&quot;">​</a></h1><p>Redis 更准确的描述是一个数据结构服务器。Redis 的这种特殊性质让它在开发人员中很受欢迎。</p><ul><li>命令行客户端</li><li>图形化桌面客户端</li><li>编程客户端</li></ul><h2 id="redis命令行客户端" tabindex="-1">Redis命令行客户端 <a class="header-anchor" href="#redis命令行客户端" aria-label="Permalink to &quot;Redis命令行客户端&quot;">​</a></h2><p>Redis安装完成后就自带了命令行客户端：redis-cli，使用方式如下：</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">redis</span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">cli </span><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">options</span><span style="color:#89DDFF;">]</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">commonds</span><span style="color:#89DDFF;">]</span></span></code></pre></div><p>其中常见的options有：</p><ul><li><code>-h 127.0.0.1</code>：指定要连接的redis节点的IP地址，默认是127.0.0.1</li><li><code>-p 6379</code>：指定要连接的redis节点的端口，默认是6379</li><li><code>-a 123321</code>：指定redis的访问密码</li></ul><p>其中的commonds就是Redis的操作命令，例如：</p><ul><li><code>ping</code>：与redis服务端做心跳测试，服务端正常会返回<code>pong</code></li><li>不指定commond时，会进入<code>redis-cli</code>的交互控制台：</li></ul><p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img/image-20211211110439353.png" alt="image-20211211110439353"></p><h2 id="resp-⭐" tabindex="-1">RESP ⭐ <a class="header-anchor" href="#resp-⭐" aria-label="Permalink to &quot;RESP ⭐&quot;">​</a></h2><p>GitHub上的大神编写了Redis的图形化桌面客户端，地址：<a href="https://github.com/uglide/RedisDesktopManager" target="_blank" rel="noreferrer">https://github.com/uglide/RedisDesktopManager</a></p><p>不过该仓库提供的是RedisDesktopManager的源码，并未提供windows安装包。</p><p>在下面这个仓库可以找到安装包：<a href="https://github.com/lework/RedisDesktopManager-Windows/releases" target="_blank" rel="noreferrer">https://github.com/lework/RedisDesktopManager-Windows/releases</a></p><p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img/image-20211211111351885.png" alt="image-20211211111351885"></p><h3 id="安装" tabindex="-1">安装 <a class="header-anchor" href="#安装" aria-label="Permalink to &quot;安装&quot;">​</a></h3><p>解压缩后，运行安装程序即可安装：</p><p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img/image-20211214155123841.png" alt="image-20211214155123841"></p><p>此处略。</p><p>安装完成后，在安装目录下找到rdm.exe文件：</p><p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img/image-20211211110935819.png" alt="image-20211211110935819"></p><p>双击即可运行：</p><p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img/image-20211214155406692.png" alt="image-20211214155406692"></p><h3 id="建立连接" tabindex="-1">建立连接 <a class="header-anchor" href="#建立连接" aria-label="Permalink to &quot;建立连接&quot;">​</a></h3><p>点击左上角的<code>连接到Redis服务器</code>按钮：</p><p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img/image-20211214155424842.png" alt="image-20211214155424842"></p><p>在弹出的窗口中填写Redis服务信息：</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img/image-20211211111614483.png" alt="image-20211211111614483" style="zoom:50%;"><p>点击确定后，在左侧菜单会出现这个链接：</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img/image-20211214155804523.png" alt="image-20211214155804523" style="zoom:67%;"><p>点击即可建立连接了：</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img/image-20211214155849495.png" alt="image-20211214155849495" style="zoom:67%;"><p>Redis默认有16个仓库，编号从0至15. 通过配置文件可以设置仓库数量，但是不超过16，并且不能自定义仓库名称。如果是基于redis-cli连接Redis服务，可以通过select命令来选择数据库：</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;"># 选择 0号库</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">select</span><span style="color:#A6ACCD;"> 0</span></span></code></pre></div><h2 id="redisinsight" tabindex="-1">RedisInsight <a class="header-anchor" href="#redisinsight" aria-label="Permalink to &quot;RedisInsight&quot;">​</a></h2><blockquote><p>测评：相比于其他可视化工具，RedisInsight 实现的功能更强大、执行效率更改，通用性更好，出了 CRUD 基本功能的支持，还支持内存分析、指标监控、发布/订阅、慢命令日志查询等等。</p></blockquote><h3 id="基本使用" tabindex="-1">基本使用 <a class="header-anchor" href="#基本使用" aria-label="Permalink to &quot;基本使用&quot;">​</a></h3><p>首先下载RedisInsight的安装包，下载地址：<a href="https://redis.com/redis-enterprise/redis-insight/" target="_blank" rel="noreferrer">https://redis.com/redis-enterprise/redis-insight/</a></p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/CKvMdchsUwnP8pq58EOF6FXibUVy19icMxmQXj5l9FKSibAZ6svdF7icHmcHfqEYaias2B62PAurPF83D4mpw4rRwicQ/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p>下载完成后直接安装即可，安装完成后在主界面选择<code>添加Redis数据库</code>；</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/CKvMdchsUwnP8pq58EOF6FXibUVy19icMxqS2PFVVicwSMUib9JG3A461AQscjib3PSu0keAnWPibURicic3iaLTIzeXowQ/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><ul><li>选择<code>手动添加数据库</code>，输入Redis服务连接信息即可；</li></ul><p><img src="https://mmbiz.qpic.cn/mmbiz_png/CKvMdchsUwnP8pq58EOF6FXibUVy19icMxM686MAuYXUVpiaUEiamiaSyb9AZg7fFIyhgbiaxADsSeEpiaVp10xcMK0Og/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p>打开连接后即可管理Redis，右上角会显示已经安装的Redis增强模块；</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/CKvMdchsUwnP8pq58EOF6FXibUVy19icMxTdIyfLM2Xb0YI6aCMiaXqYcONfGKloIlZKIpXcxzEyBjTkIicDruRB5Q/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><ul><li>接下来我们就可以通过RedisInsight在Redis中添加键值对数据了，比如添加<code>String</code>类型键值对；</li></ul><p><img src="https://mmbiz.qpic.cn/mmbiz_png/CKvMdchsUwnP8pq58EOF6FXibUVy19icMxshw1jibphPDrRibWR9ICN7a0gwW8iaUNqPndSHAOCibNDaypasuJabMqlA/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><ul><li>添加Hash类型，编辑的时候可以单个属性编辑，还是挺方便的；</li></ul><p><img src="https://mmbiz.qpic.cn/mmbiz_png/CKvMdchsUwnP8pq58EOF6FXibUVy19icMx35OYibniawGVGqJGzCsNXbDRF7wWXXhWvMu3ydFQWckNjiaQ6ZWYy6orw/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><ul><li>添加List类型，编辑的时候可以直接Push元素进去；</li></ul><p><img src="https://mmbiz.qpic.cn/mmbiz_png/CKvMdchsUwnP8pq58EOF6FXibUVy19icMxRdBEWmficDceYxBy2EoMCUaKsDGQH9TVXdNQcSKgIwlYk9aroBtRxUg/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><ul><li>添加JSON类型，安装RedisJSON模块后可支持；</li></ul><p><img src="https://mmbiz.qpic.cn/mmbiz_png/CKvMdchsUwnP8pq58EOF6FXibUVy19icMxL7m0YnSVFVN2FeoUfH4y1GZQZ4S9t1cppAjjurFsS3cTogxnfib0WDw/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><ul><li>对原生JSON类型，不仅支持高亮预览，还能支持新增、编辑和删除单个属性，够方便！</li></ul><p><img src="https://mmbiz.qpic.cn/mmbiz_png/CKvMdchsUwnP8pq58EOF6FXibUVy19icMxGicscWcibm8xotquVTGEhOyVZGHRAoAtktJsYxsLSgTDRWjuVqXnSSlQ/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><ul><li>另外RedisInsight还支持深色和浅色两种主题切换，在设置中即可更改。</li></ul><p><img src="https://mmbiz.qpic.cn/mmbiz_png/CKvMdchsUwnP8pq58EOF6FXibUVy19icMxDG4THJKoY2qec5jxj2l6TNtupscAictV2QWDrqrtZVt5yP1qagND6cQ/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><h3 id="cli" tabindex="-1">CLI <a class="header-anchor" href="#cli" aria-label="Permalink to &quot;CLI&quot;">​</a></h3><p>RedisInsight的图形化界面功能满足不了你的话，还可以试试它的<code>CLI</code>功能，点击左下角CLI标签即可打开；</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/CKvMdchsUwnP8pq58EOF6FXibUVy19icMxWXLX1kTYktynlqnArMic9w6ILH6XjZOwozFN0d8MicRsuiaUYK4WgiaB4g/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><ul><li>贴心的Redis官方怕你记不住命令，还添加了<code>Command Helper</code>这个查找命令文档的功能，比如我们可以搜索下<code>hget</code>这个命令的用法。</li></ul><p><img src="https://mmbiz.qpic.cn/mmbiz_png/CKvMdchsUwnP8pq58EOF6FXibUVy19icMxbz97adFghLkInAOJ7aftINhnpcycMqriawzNwWcuO2xwYRglPmHy6Zg/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><h3 id="profiler" tabindex="-1">Profiler <a class="header-anchor" href="#profiler" aria-label="Permalink to &quot;Profiler&quot;">​</a></h3><p>通过Profiler功能，我们可以查看Redis的命令执行日志，比如我们使用RedisInsight添加一个叫<code>testKey</code>的键值对，Profiler将显示如下日志。</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/CKvMdchsUwnP8pq58EOF6FXibUVy19icMxJjuJDkne1S2jib9qoVfMcJctt0fcbneYEU2lrhPKFHBQuSYdtMdsDxQ/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><h2 id="redis-desktop-manager" tabindex="-1">Redis Desktop Manager <a class="header-anchor" href="#redis-desktop-manager" aria-label="Permalink to &quot;Redis Desktop Manager&quot;">​</a></h2><p>Redis Desktop Manager 应该是现在使用率最广的可视化工具了，存在时间很久，经过了数次迭代，基于 Qt 5 开发，支持跨平台支持。以前是免费的，不过现在改成收费工具，试用期可以有半个月的时间，最新版的访问地址如下！</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">https://github.com/uglide/RedisDesktopManager/releases</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.1.30/202212221130279.png" alt="image-20221222113047120" style="zoom:67%;"><p>测评：界面看着比较简洁，该有的功能都有，功能很全，key 的显示可以支持按冒号分割的键名空间。</p><p><strong>免费版本的下载地址如下</strong>！</p><p>百度网盘：<code>https://pan.baidu.com/s/1rMWR-OQnfsxJ3_HSqEE8xw</code></p><p>提取码：<code>tebu</code></p><h2 id="anotherredisdesktopmanager" tabindex="-1">AnotherRedisDesktopManager <a class="header-anchor" href="#anotherredisdesktopmanager" aria-label="Permalink to &quot;AnotherRedisDesktopManager&quot;">​</a></h2><blockquote><p>AnotherRedisDesktopManager 是一款比较稳定简洁的 redis UI工具，国人开发，支持跨平台，完全免费，最新版的访问地址如下！<a href="https://github.com/qishibo/AnotherRedisDesktopManager" target="_blank" rel="noreferrer">https://github.com/qishibo/AnotherRedisDesktopManager</a></p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.1.30/202212221131517.png" alt="image-20221222113141360" style="zoom:67%;"><blockquote><p>测评：基本的功能都有，有监控统计，支持暗黑主题，还支持集群的添加，是一款非常不错的可视化客户端。</p></blockquote><h1 id="nosql数据库" tabindex="-1">NoSQL数据库 <a class="header-anchor" href="#nosql数据库" aria-label="Permalink to &quot;NoSQL数据库&quot;">​</a></h1><h2 id="sql-vs-nosql" tabindex="-1">SQL VS NoSQL <a class="header-anchor" href="#sql-vs-nosql" aria-label="Permalink to &quot;SQL VS NoSQL&quot;">​</a></h2><blockquote><p>NoSQL(NoSQL = <strong>Not Only SQL</strong> )，意即“不仅仅是SQL”，泛指<strong>非关系型的数据库</strong>。 NoSQL 不依赖业务逻辑方式存储，而以简单的key-value模式存储。因此大大的增加了数据库的扩展能力。<strong>不遵循SQL标准，不支持ACID，远超于SQL的性能</strong>。</p></blockquote><h3 id="完整对比" tabindex="-1">完整对比 <a class="header-anchor" href="#完整对比" aria-label="Permalink to &quot;完整对比&quot;">​</a></h3><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.30/202207162152624.png" alt="image-20220716215214433" style="zoom:80%;"><blockquote><p>扩展性：垂直表示没考虑数据拆分和分片，水平表示考虑到了数据拆分和分片</p></blockquote><h3 id="结构化-vs-非结构化" tabindex="-1">结构化 VS 非结构化 <a class="header-anchor" href="#结构化-vs-非结构化" aria-label="Permalink to &quot;结构化 VS 非结构化&quot;">​</a></h3><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.3.30/202306151431510.png" alt="image-20230615143140424" style="zoom:80%;"><h3 id="sql-vs-nosql-1" tabindex="-1">SQL VS NoSQL <a class="header-anchor" href="#sql-vs-nosql-1" aria-label="Permalink to &quot;SQL VS NoSQL&quot;">​</a></h3><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.3.30/202306151432041.png" alt="image-20230615143225958" style="zoom:80%;"><h2 id="redis特征" tabindex="-1">Redis特征 <a class="header-anchor" href="#redis特征" aria-label="Permalink to &quot;Redis特征&quot;">​</a></h2><p>Redis诞生于2009年全称是<strong>Remote</strong> <strong>Dictionary</strong> Server，远程词典服务器，是基于内存的键值型NoSQL数据库</p><blockquote><ul><li>键值（key-value）型，value支持多种不同数据结构，功能丰富</li><li>单线程，每个命令具备原子性</li><li>低延迟，速度快（基于内存、IO多路复用、良好的编码）。</li><li>支持数据持久化</li><li>支持主从集群、分片集群</li><li>支持多语言客户端</li></ul></blockquote><blockquote><ul><li><strong>性能极高</strong> - Redis 读速度 110000次/s，写的速度是 81000次/s。</li><li><strong>丰富的数据类型</strong> - Redis 支持的类型 String， Hash 、List 、Set 及 Ordered Set 数据类型操作。</li><li><strong>原子性</strong> - Redis 的所有操作都是原子性的，意思就是要么成功，要么失败。单个操作时原子性的。多个操作也支持事务，即原子性，通过 MULTI 和 EXEC 指令包起来。</li><li><strong>丰富的特性</strong> - Redis 还支持 publis/subscribe，通知，key 过期等等特性。</li><li><strong>高速读写</strong> ，redis 使用自己实现的分离器，代码量很短，没有使用 lock(MySQL),因此效率非常高。</li></ul></blockquote><h2 id="不同数据库适用场景" tabindex="-1">不同数据库适用场景 <a class="header-anchor" href="#不同数据库适用场景" aria-label="Permalink to &quot;不同数据库适用场景&quot;">​</a></h2><blockquote><p>对数据<strong>高并发</strong>的读写，<strong>海量</strong>数据的<strong>读写</strong>，对<strong>数据高可扩展性</strong></p></blockquote><p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img/image-20211102103837062.png" alt="image-20211102103837062"></p><h2 id="不适用场景" tabindex="-1">不适用场景 <a class="header-anchor" href="#不适用场景" aria-label="Permalink to &quot;不适用场景&quot;">​</a></h2><blockquote><ul><li><p>需要事务支持，基于sql的结构化查询存储，处理复杂的关系,需要即席查询</p></li><li><p><strong>用不着sql的和用了sql也不行的情况，请考虑用NoSql</strong></p></li></ul></blockquote><h2 id="优缺点" tabindex="-1">优缺点 <a class="header-anchor" href="#优缺点" aria-label="Permalink to &quot;优缺点&quot;">​</a></h2><h3 id="优势" tabindex="-1">优势 <a class="header-anchor" href="#优势" aria-label="Permalink to &quot;优势&quot;">​</a></h3><blockquote><ul><li>性能极高 - Redis 读速度 110000 次/s，写的速度是 81000 次/s。</li><li>丰富的数据类型 - Redis 支持的类型 String， Hash 、List 、Set 及 Ordered Set 数据类型操作。</li><li>原子性 - Redis 的所有操作都是原子性的，意思就是要么成功，要么失败。单个操作时原子性的。多个操作也支持事务，即原子性，通过 MULTI 和 EXEC 指令包起来。</li><li>丰富的特性 - Redis 还支持 publis/subscribe，通知，key 过期等等特性。</li><li>高速读写 ，redis 使用自己实现的分离器，代码量很短，没有使用 lock(MySQL),因此效率非常高。</li></ul></blockquote><h3 id="缺点" tabindex="-1">缺点 <a class="header-anchor" href="#缺点" aria-label="Permalink to &quot;缺点&quot;">​</a></h3><blockquote><ul><li>持久化。Redis 直接将数据存储到内存中，要将数据保存到磁盘上，Redis 可以使用两种方式实现持久化过程。定时快照(snapshot)：每隔一段时间将整个数据库写到磁盘上，每次均是写全部数据，代价非常高。第二种方式基于语句追加（aof）：只追踪变化的数据，但是追加的 log 可能过大，同时所有的操作均重新执行一遍，回复速度慢。</li><li>耗内存 、占用内存过高。</li></ul></blockquote><h2 id="常用nosql数据库" tabindex="-1">常用NoSQL数据库 <a class="header-anchor" href="#常用nosql数据库" aria-label="Permalink to &quot;常用NoSQL数据库&quot;">​</a></h2><h3 id="memcache" tabindex="-1">Memcache <a class="header-anchor" href="#memcache" aria-label="Permalink to &quot;Memcache&quot;">​</a></h3><blockquote><ul><li><p><strong>很早</strong>出现的NoSql数据库</p></li><li><p>数据都在内存中，一般<strong>不持久化</strong></p></li><li><p>支持简单的key-value模式，<strong>支持类型单一</strong></p></li><li><p>一般是作为<strong>缓存数据库</strong>辅助持久化的数据库</p></li></ul></blockquote><h3 id="redis-⭐" tabindex="-1">Redis ⭐ <a class="header-anchor" href="#redis-⭐" aria-label="Permalink to &quot;Redis ⭐&quot;">​</a></h3><ul><li>Redis官方网站：<a href="http://redis.io" target="_blank" rel="noreferrer">http://redis.io</a></li><li>Redis中文官方网站： <a href="http://redis.cn/" target="_blank" rel="noreferrer">http://redis.cn/</a></li></ul><blockquote><ul><li><p>几乎覆盖了Memcached的绝大部分功能</p></li><li><p>数据都在内存中，<strong>支持持久化</strong>，主要用作备份恢复</p></li><li><p>除了支持简单的key-value模式，还<strong>支持多种数据结构的存储</strong>，比如 list、set、hash、zset等。</p></li><li><p>一般是作为<strong>缓存数据库</strong>辅助持久化的数据库</p></li></ul></blockquote><h3 id="mongodb" tabindex="-1">MongoDB <a class="header-anchor" href="#mongodb" aria-label="Permalink to &quot;MongoDB&quot;">​</a></h3><blockquote><ul><li><p>高性能、开源、模式自由(schema free)的<strong>文档型数据库</strong></p></li><li><p>数据都在内存中， 如果内存不足，把不常用的数据保存到硬盘</p></li><li><p>虽然是key-value模式，但是对value（尤其是<strong>json</strong>）提供了丰富的查询功能</p></li><li><p>支持二进制数据及大型对象</p></li><li><p>可以根据数据的特点<strong>替代RDBMS</strong> ，成为独立的数据库。或者配合RDBMS，存储特定的数据。</p></li></ul></blockquote><h2 id="redis应用场景" tabindex="-1">Redis应用场景 <a class="header-anchor" href="#redis应用场景" aria-label="Permalink to &quot;Redis应用场景&quot;">​</a></h2><p><a href="https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&amp;mid=2247494383&amp;idx=2&amp;sn=e9eb08b27e8765859de5da05a360d221&amp;chksm=fc2c56e7cb5bdff1c1b6d61c7b926a61101fcb5942bd5c0af19057e7344d683b8cdb10f135fb&amp;mpshare=1&amp;scene=23&amp;srcid=04155g4cEWhQL4U4L1y4F36R&amp;sharer_sharetime=1650025400646&amp;sharer_shareid=29b8a04db1dbd975e3bf4e9f47e7ac67#rd" target="_blank" rel="noreferrer">16 个 Redis 常见使用场景！ (qq.com)</a></p><h3 id="抽奖" tabindex="-1">抽奖 <a class="header-anchor" href="#抽奖" aria-label="Permalink to &quot;抽奖&quot;">​</a></h3><p>自带一个随机获得值，Spop 命令用于移除集合中的指定 key 的一个或多个随机元素，移除后会返回移除的元素</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">spop myset</span></span></code></pre></div><h3 id="点赞、签到、打卡" tabindex="-1">点赞、签到、打卡 <a class="header-anchor" href="#点赞、签到、打卡" aria-label="Permalink to &quot;点赞、签到、打卡&quot;">​</a></h3><p>假如上面的微博ID是t1001，用户ID是u3001，用 like:t1001 来维护 t1001 这条微博的所有点赞用户</p><ul><li>点赞了这条微博：sadd like:t1001 u3001</li><li>取消点赞：srem like:t1001 u3001</li><li>是否点赞：sismember like:t1001 u3001</li><li>点赞的所有用户：smembers like:t1001</li><li>点赞数：scard like:t1001</li></ul><p>是不是比数据库简单多了。</p><h3 id="商品标签" tabindex="-1">商品标签 <a class="header-anchor" href="#商品标签" aria-label="Permalink to &quot;商品标签&quot;">​</a></h3><p>老规矩，用 tags:i5001 来维护商品所有的标签。</p><ul><li>sadd tags:i5001 画面清晰细腻</li><li>sadd tags:i5001 真彩清晰显示屏</li><li>sadd tags:i5001 流程至极</li></ul><h3 id="商品筛选" tabindex="-1">商品筛选 <a class="header-anchor" href="#商品筛选" aria-label="Permalink to &quot;商品筛选&quot;">​</a></h3><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight has-diff"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 获取差集</span></span>
<span class="line"><span style="color:#A6ACCD;">sdiff set1 set2</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 获取交集（intersection ）</span></span>
<span class="line"><span style="color:#A6ACCD;">sinter set1 set2</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 获取并集</span></span>
<span class="line"><span style="color:#A6ACCD;">sunion set1 set2</span></span></code></pre></div><p><img src="https://mmbiz.qpic.cn/mmbiz_png/eQPyBffYbudegdrhbGahVfBv4MHUH1tywAUjc9qN73ZmEUiaGhzg1BZD5FXX2v5sVqyI2jrVUmm88MDs34eicmGQ/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p>假如：iPhone11 上市了</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">sadd brand:apple iPhone11</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">sadd brand:ios iPhone11</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">sad screensize:</span><span style="color:#F78C6C;">6.0</span><span style="color:#89DDFF;">-</span><span style="color:#F78C6C;">6.24</span><span style="color:#A6ACCD;"> iPhone11</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">sad screentype:lcd iPhone </span><span style="color:#F78C6C;">11</span></span></code></pre></div><p>赛选商品，苹果的、ios的、屏幕在6.0-6.24之间的，屏幕材质是LCD屏幕</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">sinter brand:apple brand:ios screensize:</span><span style="color:#F78C6C;">6.0</span><span style="color:#89DDFF;">-</span><span style="color:#F78C6C;">6.24</span><span style="color:#A6ACCD;"> screentype:lcd</span></span></code></pre></div><h3 id="用户关注、推荐模型" tabindex="-1">用户关注、推荐模型 <a class="header-anchor" href="#用户关注、推荐模型" aria-label="Permalink to &quot;用户关注、推荐模型&quot;">​</a></h3><p>follow 关注 fans 粉丝</p><p>相互关注：</p><ul><li>sadd 1:follow 2</li><li>sadd 2:fans 1</li><li>sadd 1:fans 2</li><li>sadd 2:follow 1</li></ul><p>我关注的人也关注了他(取交集)：</p><ul><li>sinter 1:follow 2:fans</li></ul><p>可能认识的人：</p><ul><li>用户1可能认识的人(差集)：sdiff 2:follow 1:follow</li><li>用户2可能认识的人：sdiff 1:follow 2:follow</li></ul><h3 id="缓存" tabindex="-1">缓存 <a class="header-anchor" href="#缓存" aria-label="Permalink to &quot;缓存&quot;">​</a></h3><blockquote><p>缓存现在几乎是所有大中型网站都在用的必杀技，合理利用缓存提升网站的访问速度，还能大大降低数据库的访问压力。Redis 提供了键过期功能，也提供了灵活的键淘汰策略，所以，现在 Redis 用在缓存的场合非常多。</p></blockquote><h3 id="排行榜" tabindex="-1">排行榜 <a class="header-anchor" href="#排行榜" aria-label="Permalink to &quot;排行榜&quot;">​</a></h3><blockquote><p>Redis 提供的<code>有序集合</code>数据类结构能够实现复杂的排行榜应用。</p><p>id 为6001 的新闻点击数加1：<code>zincrby hotNews:20190926 1 n6001</code></p><p>获取今天点击最多的15条：<code>zrevrange hotNews:20190926 0 15 withscores</code></p><p><strong>可以作为数据库，缓存热点数据(经常被查询，但是不经常被修改或者删除的数据)和消息中间件等大部分功能</strong></p></blockquote><h3 id="计数器" tabindex="-1">计数器 <a class="header-anchor" href="#计数器" aria-label="Permalink to &quot;计数器&quot;">​</a></h3><blockquote><p>视频网站的播放量，每次浏览 +1，并发量高时如果每次都请求数据库操作无疑有很大挑战和压力。Redis 提供的 incr 命令来实现计数器功能，内存操作，性能非常好，非常适用于这些技术场景。</p></blockquote><h3 id="分布式会话" tabindex="-1">分布式会话 <a class="header-anchor" href="#分布式会话" aria-label="Permalink to &quot;分布式会话&quot;">​</a></h3><blockquote><p>相对复杂的系统中，一般都会搭建 Redis 等内存数据库为中心的 session 服务，session 不再由容器管理，而是由 session 服务及内存数据管理。</p></blockquote><h3 id="分布式锁" tabindex="-1">分布式锁 <a class="header-anchor" href="#分布式锁" aria-label="Permalink to &quot;分布式锁&quot;">​</a></h3><blockquote><p>在并发高的场合中，可以利用 Redis 的 setnx 功能来编写分布式的锁，如果设置返回 1，说明获取锁成功，否则获取锁失败。</p></blockquote><h3 id="社交网络" tabindex="-1">社交网络 <a class="header-anchor" href="#社交网络" aria-label="Permalink to &quot;社交网络&quot;">​</a></h3><blockquote><p>点赞、踩、关注/被关注，共同好友等是社交网站的基本功能，社交网站的访问量通常来说比较大，而且传统的关系数据库不适合这种类型的数据，Redis 提供的哈希，集合等数据结构能很方便的实现这些功能。</p></blockquote><h3 id="最新列表" tabindex="-1">最新列表 <a class="header-anchor" href="#最新列表" aria-label="Permalink to &quot;最新列表&quot;">​</a></h3><blockquote><p>Redis 列表结构，LPUSH 可以在列表头部插入一个内容 ID 作为关键字，LTRIM 可以用来限制列表的数量，这样列表永远为 N ，无需查询最新的列表，直接根据 ID 去到对应的内容也即可。</p></blockquote><h3 id="消息系统" tabindex="-1">消息系统 <a class="header-anchor" href="#消息系统" aria-label="Permalink to &quot;消息系统&quot;">​</a></h3><blockquote><p>消息队列是网站经常用的中间件，如 ActiveMQ，RabbitMQ，Kafaka 等流行的消息队列中间件，主要用于业务解耦，流量削峰及异步处理试试性低的业务。Redis 提供了发布/订阅及阻塞队列功能，能实现一个简单的消息队列系统。另外，这个不能和专业的消息中间件相比。</p></blockquote><h1 id="基本数据类型" tabindex="-1">基本数据类型 <a class="header-anchor" href="#基本数据类型" aria-label="Permalink to &quot;基本数据类型&quot;">​</a></h1><p>学习网址：<a href="http://www.redis.cn/commands.html" target="_blank" rel="noreferrer">http://www.redis.cn/commands.html</a></p><blockquote><p>Redis是一个key-value的数据库，key一般是String类型，不过value的类型多种多样，Redis为了方便我们学习，将操作不同数据类型的命令也做了分组，在官网（ <a href="https://redis.io/commands" target="_blank" rel="noreferrer">https://redis.io/commands </a>）可以查看</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.5/202205042041390.png" alt="image-20220504204159309" style="zoom:67%;"><h2 id="基础命令" tabindex="-1">基础命令 <a class="header-anchor" href="#基础命令" aria-label="Permalink to &quot;基础命令&quot;">​</a></h2><h3 id="查看redis版本" tabindex="-1">查看redis版本 <a class="header-anchor" href="#查看redis版本" aria-label="Permalink to &quot;查看redis版本&quot;">​</a></h3><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">redis-server</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-v</span></span>
<span class="line"><span style="color:#FFCB6B;">redis-server</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">--version</span></span></code></pre></div><h3 id="关闭redis" tabindex="-1">关闭Redis <a class="header-anchor" href="#关闭redis" aria-label="Permalink to &quot;关闭Redis&quot;">​</a></h3><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;"># 通过shutdown关闭，或者进入redis-cli后shutdown</span></span>
<span class="line"><span style="color:#FFCB6B;">redis-cli</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">shutdown</span></span>
<span class="line"><span style="color:#FFCB6B;">shutdown</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 直接关闭端口</span></span>
<span class="line"><span style="color:#FFCB6B;">ps</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-ef</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">|</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">grep</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">redis</span></span>
<span class="line"><span style="color:#82AAFF;">kill</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-9</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">redisPid</span></span></code></pre></div><h3 id="进入redis" tabindex="-1">进入Redis <a class="header-anchor" href="#进入redis" aria-label="Permalink to &quot;进入Redis&quot;">​</a></h3><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">redis-cli</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-h</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">192.168</span><span style="color:#C3E88D;">.31.202</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-p</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">6379</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-a</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">315217</span></span>
<span class="line"><span style="color:#FFCB6B;">redis-cli</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-h</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">192.168</span><span style="color:#C3E88D;">.22.130</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-p</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">6379</span></span>
<span class="line"><span style="color:#FFCB6B;">redis-cli</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-p</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">6379</span></span>
<span class="line"><span style="color:#FFCB6B;">redis-cli</span></span></code></pre></div><h3 id="数据库命令" tabindex="-1">数据库命令 <a class="header-anchor" href="#数据库命令" aria-label="Permalink to &quot;数据库命令&quot;">​</a></h3><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">-- select   数字 切换数据库</span></span>
<span class="line"><span style="color:#A6ACCD;">seletc </span><span style="color:#F78C6C;">3</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- dbsize   查看当前数据库的key的数量</span></span>
<span class="line"><span style="color:#A6ACCD;">dbsize</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- flushdb  清空当前库</span></span>
<span class="line"><span style="color:#A6ACCD;">flushdb</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- flushall 通杀全部库</span></span>
<span class="line"><span style="color:#A6ACCD;">flushall</span></span></code></pre></div><h2 id="键-key" tabindex="-1">键(key) <a class="header-anchor" href="#键-key" aria-label="Permalink to &quot;键(key)&quot;">​</a></h2><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">-- 查看当前库所有key,也可以模糊查询，生产环境下不建议使用</span></span>
<span class="line"><span style="color:#A6ACCD;">keys </span><span style="color:#89DDFF;">*</span></span>
<span class="line"><span style="color:#A6ACCD;">keys </span><span style="color:#89DDFF;">*</span><span style="color:#A6ACCD;">name</span><span style="color:#89DDFF;">*</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 判断某个key是否存在</span></span>
<span class="line"><span style="color:#A6ACCD;">exists key</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 查看你的key是什么类型</span></span>
<span class="line"><span style="color:#82AAFF;">type</span><span style="color:#A6ACCD;"> key</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 删除指定的key数据，一个或多个，用空格隔开</span></span>
<span class="line"><span style="color:#A6ACCD;">del key</span></span>
<span class="line"><span style="color:#A6ACCD;">del k1 k2 k3</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 根据value选择非阻塞删除,仅将keys从keyspace元数据中删除，真正的删除会在后续异步操作</span></span>
<span class="line"><span style="color:#A6ACCD;">unlink key</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 为给定的key设置过期时间,单位秒</span></span>
<span class="line"><span style="color:#A6ACCD;">expire key </span><span style="color:#F78C6C;">10</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 移除key的过期时间，再次用ttl key查看，为-1表示永不过期</span></span>
<span class="line"><span style="color:#A6ACCD;">persist key</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 查看还有多少秒过期，-1表示永不过期，-2表示已过期</span></span>
<span class="line"><span style="color:#A6ACCD;">ttl key</span></span></code></pre></div><h2 id="字符串-string" tabindex="-1">字符串(string) <a class="header-anchor" href="#字符串-string" aria-label="Permalink to &quot;字符串(string)&quot;">​</a></h2><blockquote><p>String是Redis最基本的类型，一个key对应一个value。String类型是<strong>二进制安全的</strong>。意味着Redis的string<strong>可以包含任何数据</strong>。比如jpg图片或者序列化的对象。其value是字符串，不过根据字符串的格式不同，又可以分为3类：</p></blockquote><blockquote><ul><li>string：普通字符串</li><li>int：整数类型，可以做自增、自减操作</li><li>float：浮点类型，可以做自增、自减操作</li></ul></blockquote><blockquote><p>不管哪种格式，底层都是字节数组形式存储，只不过是编码方式不同。字符串的最大不能超过512m</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.5/202205042048062.png" alt="image-20220504204828001" style="zoom:67%;"><h3 id="key命名要求" tabindex="-1">key命名要求 <a class="header-anchor" href="#key命名要求" aria-label="Permalink to &quot;key命名要求&quot;">​</a></h3><blockquote><p>Redis没有类似MySQL中的Table的概念，我们该如何区分不同类型的key呢？例如，需要存储用户、商品信息到redis，有一个用户id是1，有一个商品id恰好也是1。<strong>Redis的key允许有多个单词形成层级结构，多个单词之间用&#39;:&#39;隔开</strong>，格式如下：</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.30/202207162215899.png" alt="image-20220716221546811" style="zoom:80%;"><blockquote><p>这个格式并非固定，也可以根据自己的需求来删除或添加词条。例如我们的项目名称叫 heima，有user和product两种不同类型的数据，我们可以这样定义key：</p></blockquote><blockquote><ul><li>user相关的key：<code>heima:user:1</code></li><li>product相关的key：<code>heima:product:1</code></li></ul></blockquote><blockquote><p>如果Value是一个Java对象，例如一个User对象，则可以将对象序列化为JSON字符串后存储：</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.5/202205042053383.png" alt="image-20220504205310323" style="zoom:80%;"><h3 id="命令语法" tabindex="-1">命令语法 <a class="header-anchor" href="#命令语法" aria-label="Permalink to &quot;命令语法&quot;">​</a></h3><h4 id="添加查询" tabindex="-1">添加查询 <a class="header-anchor" href="#添加查询" aria-label="Permalink to &quot;添加查询&quot;">​</a></h4><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">-- 添加键值对</span></span>
<span class="line"><span style="color:#A6ACCD;">set key value </span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 查询对应键值</span></span>
<span class="line"><span style="color:#A6ACCD;">get key </span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 同时设置一个或多个 key-value对 </span></span>
<span class="line"><span style="color:#A6ACCD;">mset k1 v1 k2 v2 </span><span style="color:#89DDFF;">...</span><span style="color:#A6ACCD;">  </span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 同时获取一个或多个 value </span></span>
<span class="line"><span style="color:#A6ACCD;">mget key1 key2 key3 </span><span style="color:#89DDFF;">...</span><span style="color:#A6ACCD;">  </span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 以新换旧，设置了新值同时获得旧值</span></span>
<span class="line"><span style="color:#A6ACCD;">getset key value </span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">--将给定的value追加到原值的末尾</span></span>
<span class="line"><span style="color:#A6ACCD;">append key value</span></span></code></pre></div><h4 id="自增自减" tabindex="-1">自增自减 <a class="header-anchor" href="#自增自减" aria-label="Permalink to &quot;自增自减&quot;">​</a></h4><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">--将key中储存的数字值增减1,只能对数字值操作，如果为空，新增值为1</span></span>
<span class="line"><span style="color:#A6ACCD;">incr key  </span></span>
<span class="line"><span style="color:#A6ACCD;">decr key </span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 将key中储存的数字值增减 ，自定义步长，比如1，2，3，4</span></span>
<span class="line"><span style="color:#A6ACCD;">incrby key 步长 </span></span>
<span class="line"><span style="color:#A6ACCD;">decrby key 步长</span></span></code></pre></div><h4 id="组合命令" tabindex="-1">组合命令 <a class="header-anchor" href="#组合命令" aria-label="Permalink to &quot;组合命令&quot;">​</a></h4><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">-- setnx：添加一个String类型的键值对，前提是这个key不存在，否则不执行</span></span>
<span class="line"><span style="color:#A6ACCD;">setnx key value </span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 同时设置一个或多个 key-value 对，当且仅当所有给定 key 都不存在</span></span>
<span class="line"><span style="color:#A6ACCD;">msetnx k1 v1 k2 v2  </span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- setex：添加一个String类型的键值对，并且指定有效期，单位秒</span></span>
<span class="line"><span style="color:#A6ACCD;">setex key 过期时间 value</span></span>
<span class="line"><span style="color:#A6ACCD;">setex name </span><span style="color:#F78C6C;">10</span><span style="color:#A6ACCD;"> jack</span></span>
<span class="line"><span style="color:#A6ACCD;">set name jack ex </span><span style="color:#F78C6C;">10</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 设置k1，超时时间10s</span></span>
<span class="line"><span style="color:#A6ACCD;">set k1 v1 xx ex </span><span style="color:#F78C6C;">10</span></span></code></pre></div><h4 id="其他命令" tabindex="-1">其他命令 <a class="header-anchor" href="#其他命令" aria-label="Permalink to &quot;其他命令&quot;">​</a></h4><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">-- 获得值的长度</span></span>
<span class="line"><span style="color:#A6ACCD;">strlen key </span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 获得value值，起始位置一般为0，结束位置为-1,注意，一个key对应一个value</span></span>
<span class="line"><span style="color:#A6ACCD;">getrange  key 起始位置 结束位置  </span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 用value覆写key所储存的字符串值，从&lt;起始位置&gt;开始(索引从0开始)</span></span>
<span class="line"><span style="color:#A6ACCD;">setrange key 起始位置 value</span></span></code></pre></div><h3 id="应用场景" tabindex="-1">应用场景 <a class="header-anchor" href="#应用场景" aria-label="Permalink to &quot;应用场景&quot;">​</a></h3><h4 id="缓存对象" tabindex="-1">缓存对象 <a class="header-anchor" href="#缓存对象" aria-label="Permalink to &quot;缓存对象&quot;">​</a></h4><p>使用 String 来缓存对象有两种方式：</p><blockquote><p>直接缓存整个对象的 JSON，</p></blockquote><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">set </span><span style="color:#FFCB6B;">heima</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">user</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">{&quot;name&quot;:&quot;xiaolin&quot;, &quot;age&quot;:18}</span><span style="color:#89DDFF;">&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">set </span><span style="color:#FFCB6B;">heima</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">user</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">{&quot;name&quot;:&quot;renshuo&quot;, &quot;age&quot;:22}</span><span style="color:#89DDFF;">&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">set </span><span style="color:#FFCB6B;">heima</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">product</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">{&quot;id&quot;:1, &quot;name&quot;:&quot;小米11&quot;, &quot;price&quot;: 4999}</span><span style="color:#89DDFF;">&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">set </span><span style="color:#FFCB6B;">heima</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">product</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">{&quot;id&quot;:2, &quot;name&quot;:&quot;荣耀6&quot;, &quot;price&quot;: 2999}</span><span style="color:#89DDFF;">&#39;</span></span></code></pre></div><blockquote><p>采用将 key 进行分离为 user:ID:属性，采用 MSET 存储，用 MGET 获取各属性值，命令例子：</p></blockquote><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">mset </span><span style="color:#FFCB6B;">heima</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">user</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">3</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">name</span><span style="color:#A6ACCD;"> xiaolin </span><span style="color:#FFCB6B;">heima</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">user</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">3</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">age</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">18</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">heima</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">user</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">3</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">name</span><span style="color:#A6ACCD;"> xiaomei </span><span style="color:#FFCB6B;">heima</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">user</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">3</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">age</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">20</span></span></code></pre></div><h4 id="常规计数" tabindex="-1">常规计数 <a class="header-anchor" href="#常规计数" aria-label="Permalink to &quot;常规计数&quot;">​</a></h4><p>因为 Redis 处理命令是单线程，所以执行命令的过程是原子的。因此 String 数据类型适合计数场景，比如计算访问次数、点赞、转发、库存数量等等。</p><p>比如计算文章的阅读量：</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 初始化文章的阅读量</span></span>
<span class="line"><span style="color:#A6ACCD;">set aritcle:readcount:</span><span style="color:#F78C6C;">1001</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 阅读量</span><span style="color:#89DDFF;">+</span><span style="color:#F78C6C;">1</span></span>
<span class="line"><span style="color:#A6ACCD;">incr aritcle:readcount:</span><span style="color:#F78C6C;">1001</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 阅读量</span><span style="color:#89DDFF;">+</span><span style="color:#F78C6C;">1</span></span>
<span class="line"><span style="color:#A6ACCD;">incr aritcle:readcount:</span><span style="color:#F78C6C;">1001</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 阅读量</span><span style="color:#89DDFF;">+</span><span style="color:#F78C6C;">1</span></span>
<span class="line"><span style="color:#A6ACCD;">incr aritcle:readcount:</span><span style="color:#F78C6C;">1001</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 获取对应文章的阅读量 </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">3</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#A6ACCD;">get aritcle:readcount:</span><span style="color:#F78C6C;">1001</span></span></code></pre></div><h4 id="分布式锁-1" tabindex="-1">分布式锁 <a class="header-anchor" href="#分布式锁-1" aria-label="Permalink to &quot;分布式锁&quot;">​</a></h4><p>SET 命令有个 NX 参数可以实现「key不存在才插入」，可以用它来实现分布式锁：</p><ul><li>如果 key 不存在，则显示插入成功，可以用来表示加锁成功；</li><li>如果 key 存在，则会显示插入失败，可以用来表示加锁失败。</li></ul><p>一般而言，还会对分布式锁加上过期时间，分布式锁的命令如下：</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">SET lock_key unique_value NX PX </span><span style="color:#F78C6C;">10000</span></span></code></pre></div><ul><li>lock_key 就是 key 键；</li><li>unique_value 是客户端生成的唯一的标识；</li><li>NX 代表只在 lock_key 不存在时，才对 lock_key 进行设置操作；</li><li>PX 10000 表示设置 lock_key 的过期时间为 10s，这是为了避免客户端发生异常而无法释放锁。</li></ul><p>而解锁的过程就是将 lock_key 键删除，但不能乱删，要保证执行操作的客户端就是加锁的客户端。所以，解锁的时候，我们要先判断锁的 unique_value 是否为加锁客户端，是的话，才将 lock_key 键删除。</p><p>可以看到，解锁是有两个操作，这时就需要 Lua 脚本来保证解锁的原子性，因为 Redis 在执行 Lua 脚本时，可以以原子性的方式执行，保证了锁释放操作的原子性。</p><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">-- 释放锁时，先比较 unique_value 是否相等，避免锁的误释放</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">if</span><span style="color:#A6ACCD;"> redis.</span><span style="color:#82AAFF;">call</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">get</span><span style="color:#89DDFF;">&quot;</span><span style="color:#A6ACCD;">,KEYS[</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;">]) </span><span style="color:#89DDFF;">==</span><span style="color:#A6ACCD;"> ARGV[</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;">] </span><span style="color:#89DDFF;font-style:italic;">then</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#A6ACCD;"> redis.</span><span style="color:#82AAFF;">call</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">del</span><span style="color:#89DDFF;">&quot;</span><span style="color:#A6ACCD;">,KEYS[</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;">])</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">else</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">end</span></span></code></pre></div><p>这样一来，就通过使用 SET 命令和 Lua 脚本在 Redis 单节点上完成了分布式锁的加锁和解锁。</p><h3 id="数据结构" tabindex="-1">数据结构 <a class="header-anchor" href="#数据结构" aria-label="Permalink to &quot;数据结构&quot;">​</a></h3><p>String的数据结构为<strong>简单动态字符串</strong>(Simple Dynamic String,缩写SDS)。是可以修改的字符串，内部结构实现上类似于Java的ArrayList，采用预分配冗余空间的方式来减少内存的频繁分配.</p><p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img/image-20210916191943760.png" alt="image-20210916191943760"></p><p>如图中所示，内部为当前字符串实际分配的空间capacity一般要高于实际字符串长度len。当字符串长度小于1M时，扩容都是加倍现有的空间，如果超过1M，扩容时一次只会多扩1M的空间。需要注意的是字符串最大长度为512M。</p><blockquote><p>SDS 不仅可以保存文本数据，还可以保存二进制数据。因为 <code>SDS</code> 使用 <code>len</code> 属性的值而不是空字符来判断字符串是否结束，并且 SDS 的所有 API 都会以处理二进制的方式来处理 SDS 存放在 <code>buf[]</code> 数组里的数据。所以 SDS 不光能存放文本数据，而且能保存图片、音频、视频、压缩文件这样的二进制数据</p></blockquote><blockquote><p>SDS 获取字符串长度的时间复杂度是 O(1)。因为 C 语言的字符串并不记录自身长度，所以获取长度的复杂度为 O(n)；而 SDS 结构里用 <code>len</code> 属性记录了字符串长度，所以复杂度为 <code>O(1)</code>。</p></blockquote><blockquote><p>Redis 的 SDS API 是安全的，拼接字符串不会造成缓冲区溢出。因为 SDS 在拼接字符串之前会检查 SDS 空间是否满足要求，如果空间不够会自动扩容，所以不会导致缓冲区溢出的问题。</p></blockquote><h2 id="列表-list" tabindex="-1">列表(List) <a class="header-anchor" href="#列表-list" aria-label="Permalink to &quot;列表(List)&quot;">​</a></h2><blockquote><p>Redis 列表是简单的字符串列表，按照插入顺序排序。你可以添加一个元素到列表的头部（左边）或者尾部（右边）。它的底层实际是个<strong>双向链表</strong>，对两端的操作性能很高，通过索引下标的操作中间的节点性能会较差。常用来存储一个有序数据，例如：<strong>朋友圈点赞列表，评论列表等</strong>。</p></blockquote><blockquote><p>List 列表是简单的字符串列表，<strong>按照插入顺序排序</strong>，可以从头部或尾部向 List 列表添加元素。列表的最大长度为 <code>2^32 - 1</code>，也即每个列表支持超过 <code>40 亿</code>个元素。</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.30/202207162228468.png" alt="image-20220716222830385" style="zoom:80%;"><p>Redis中的List类型与Java中的LinkedList类似。既可以支持正向检索和也可以支持反向检索。</p><blockquote><p>有序、元素可以重复、、插入和删除快、查询速度一般</p></blockquote><h3 id="常用命令" tabindex="-1">常用命令 <a class="header-anchor" href="#常用命令" aria-label="Permalink to &quot;常用命令&quot;">​</a></h3><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">-- 从左边/右边插入一个或多个值</span></span>
<span class="line"><span style="color:#A6ACCD;">lpush k1 v1 v2 v3 .... </span></span>
<span class="line"><span style="color:#A6ACCD;">rpush k1 v1 v2 v3 .... </span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 从左边/右边吐出一个值,吐出值后元素就不在列表中了。值在键在，值亡键亡</span></span>
<span class="line"><span style="color:#A6ACCD;">lpop</span><span style="color:#89DDFF;">/</span><span style="color:#A6ACCD;">rpop k1</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 从&lt;key1&gt;列表右边吐出一个值，插到&lt;key2&gt;列表左边。</span></span>
<span class="line"><span style="color:#A6ACCD;">rpoplpush </span><span style="color:#C792EA;">&lt;key1&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;key2&gt;</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- BLPOP和BRPOP：与LPOP和RPOP类似，只不过在没有元素时等待指定时间（下设100s），而不是直接返回nil</span></span>
<span class="line"><span style="color:#A6ACCD;">blpop k1 </span><span style="color:#F78C6C;">100</span></span>
<span class="line"><span style="color:#A6ACCD;">brpop k1 </span><span style="color:#F78C6C;">100</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 按照索引下标获得元素(从左到右)</span></span>
<span class="line"><span style="color:#A6ACCD;">lrange </span><span style="color:#C792EA;">&lt;key&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;start&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;stop&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 0左边第一个，-1右边第一个，（0-1表示获取所有）</span></span>
<span class="line"><span style="color:#A6ACCD;">lrange mylist </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">-</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> </span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 按照索引下标获得元素(从左到右)</span></span>
<span class="line"><span style="color:#A6ACCD;">lindex </span><span style="color:#C792EA;">&lt;key&gt;&lt;index&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 获得列表长度 </span></span>
<span class="line"><span style="color:#A6ACCD;">llen </span><span style="color:#C792EA;">&lt;key&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 在&lt;value&gt;的后面插入&lt;newvalue&gt;插入值</span></span>
<span class="line"><span style="color:#A6ACCD;">linsert </span><span style="color:#C792EA;">&lt;key&gt;</span><span style="color:#A6ACCD;"> before </span><span style="color:#C792EA;">&lt;value&gt;&lt;newvalue&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 从左边删除n个value(从左到右)，value表示多个相同的value</span></span>
<span class="line"><span style="color:#A6ACCD;">lrem </span><span style="color:#C792EA;">&lt;key&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;n&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;value&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 将列表key下标为index的值替换成value</span></span>
<span class="line"><span style="color:#A6ACCD;">lset </span><span style="color:#C792EA;">&lt;key&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;index&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;value&gt;</span></span></code></pre></div><h3 id="数据结构-1" tabindex="-1">数据结构 <a class="header-anchor" href="#数据结构-1" aria-label="Permalink to &quot;数据结构&quot;">​</a></h3><blockquote><p>List的数据结构为快速链表quickList。首先在列表元素较少的情况下会使用一块连续的内存存储，这个结构是ziplist，也即是压缩列表。它将所有的元素紧挨着一起存储，分配的是一块连续的内存。</p></blockquote><blockquote><p>当数据量比较多的时候才会改成quicklist。因为普通的链表需要的附加指针空间太大，会比较浪费空间。比如这个列表里存的只是int类型的数据，结构上还需要两个额外的指针prev和next。</p></blockquote><p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img/image-20210916194212615.png" alt="image-20210916194212615"></p><blockquote><p>Redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用。这样既满足了快速的插入删除性能，又不会出现太大的空间冗余。</p></blockquote><h2 id="集合-set" tabindex="-1">集合(Set) <a class="header-anchor" href="#集合-set" aria-label="Permalink to &quot;集合(Set)&quot;">​</a></h2><blockquote><p>Redis set对外提供的功能与list类似是一个列表的功能，特殊之处在于set是可以<strong>自动去重</strong>的，当你需要存储一个列表数据，又不希望出现重复数据时，set是一个很好的选择，并且<strong>set提供了判断某个成员是否在一个set集合内</strong>的重要方法，这个也是list所不能提供的。</p></blockquote><blockquote><p>Redis的Set是string类型的无序集合。它底层其实是一个value为null的hash表，所以添加，删除，查找的复杂度都是O(1)一个集合最多可以存储 <code>2^32-1</code> 个元素</p></blockquote><blockquote><ul><li><strong>无序、元素不可重复、查找快、支持交集、并集、差集等功能</strong></li><li><strong>List 可以存储重复元素，Set 只能存储非重复元素</strong></li><li><strong>List 是按照元素的先后顺序存储元素的，而 Set 则是无序方式存储元素的</strong></li></ul></blockquote><h3 id="常见命令" tabindex="-1">常见命令 <a class="header-anchor" href="#常见命令" aria-label="Permalink to &quot;常见命令&quot;">​</a></h3><h4 id="增删查" tabindex="-1">增删查 <a class="header-anchor" href="#增删查" aria-label="Permalink to &quot;增删查&quot;">​</a></h4><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">-- 将一个或多个 member 元素加入到集合 key 中，已经存在的 member 元素将被忽略</span></span>
<span class="line"><span style="color:#A6ACCD;">sadd  </span><span style="color:#C792EA;">&lt;key&gt;</span><span style="color:#A6ACCD;">  </span><span style="color:#C792EA;">&lt;value1&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;value2&gt;</span><span style="color:#A6ACCD;"> ..... </span></span>
<span class="line"><span style="color:#A6ACCD;">sadd s1 a b c</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 取出该集合的所有值</span></span>
<span class="line"><span style="color:#A6ACCD;">smembers </span><span style="color:#C792EA;">&lt;key&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">smembers s1</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 删除集合中的某个元素</span></span>
<span class="line"><span style="color:#A6ACCD;">srem </span><span style="color:#C792EA;">&lt;key&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;value1&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;value2&gt;</span><span style="color:#A6ACCD;"> ....</span></span>
<span class="line"><span style="color:#A6ACCD;">srem s1 a</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 判断集合&lt;key&gt;是否为含有该&lt;value&gt;值，有1，没有0</span></span>
<span class="line"><span style="color:#A6ACCD;">sismember </span><span style="color:#C792EA;">&lt;key&gt;&lt;value&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">sismember s1 a</span></span></code></pre></div><h4 id="交并差集⭐" tabindex="-1">交并差集⭐ <a class="header-anchor" href="#交并差集⭐" aria-label="Permalink to &quot;交并差集⭐&quot;">​</a></h4><blockquote><p>使用详见下面的实战案例</p></blockquote><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight has-diff"><code><span class="line"><span style="color:#676E95;font-style:italic;">-- 返回两个集合的交集元素</span></span>
<span class="line"><span style="color:#A6ACCD;">sinter </span><span style="color:#C792EA;">&lt;key1&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;key2&gt;</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 将交集结果存入新集合destination中</span></span>
<span class="line"><span style="color:#A6ACCD;">sinterstore destination key [key </span><span style="color:#89DDFF;">...</span><span style="color:#A6ACCD;">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 返回两个集合的并集元素</span></span>
<span class="line"><span style="color:#A6ACCD;">sunion </span><span style="color:#C792EA;">&lt;key1&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;key2&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 将并集结果存入新集合destination中</span></span>
<span class="line"><span style="color:#A6ACCD;">sunionstore destination key [key </span><span style="color:#89DDFF;">...</span><span style="color:#A6ACCD;">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 返回两个集合的差集元素(key1中的，不包含key2中的)</span></span>
<span class="line"><span style="color:#A6ACCD;">sdiff </span><span style="color:#C792EA;">&lt;key1&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;key2&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 将差集结果存入新集合destination中</span></span>
<span class="line"><span style="color:#A6ACCD;">sdiffstore destination key [key </span><span style="color:#89DDFF;">...</span><span style="color:#A6ACCD;">]</span></span></code></pre></div><h4 id="其他命令-1" tabindex="-1">其他命令 <a class="header-anchor" href="#其他命令-1" aria-label="Permalink to &quot;其他命令&quot;">​</a></h4><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">-- 返回该集合的元素个数</span></span>
<span class="line"><span style="color:#A6ACCD;">scard </span><span style="color:#C792EA;">&lt;key&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 随机从该集合中吐出一个值，从集合key中随机选出count个元素，元素从key中删除</span></span>
<span class="line"><span style="color:#A6ACCD;">spop </span><span style="color:#C792EA;">&lt;key&gt;</span><span style="color:#A6ACCD;"> </span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 随机从该集合中取出n个值。不会从集合中删除</span></span>
<span class="line"><span style="color:#A6ACCD;">srandmember </span><span style="color:#C792EA;">&lt;key&gt;&lt;n&gt;</span><span style="color:#A6ACCD;"> </span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 把集合中一个值从一个集合移动到另一个集合</span></span>
<span class="line"><span style="color:#A6ACCD;">smove </span><span style="color:#C792EA;">&lt;source&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;destination&gt;</span><span style="color:#A6ACCD;"> value</span></span></code></pre></div><h3 id="实战演练-⭐" tabindex="-1">实战演练 ⭐ <a class="header-anchor" href="#实战演练-⭐" aria-label="Permalink to &quot;实战演练 ⭐&quot;">​</a></h3><blockquote><p>将下列数据用Redis的Set集合来存储：</p></blockquote><blockquote><p>张三的好友有：李四、王五、赵六</p></blockquote><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">sadd zs lisi wangwu zhaoliu</span></span></code></pre></div><blockquote><p>李四的好友有：王五、麻子、二狗</p></blockquote><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">sadd ls wangwu mazi ergou</span></span></code></pre></div><p>利用Set的命令实现下列功能：</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight has-diff"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 计算张三的好友有几人，</span><span style="color:#F78C6C;">3</span></span>
<span class="line"><span style="color:#A6ACCD;">scard zs</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 计算张三和李四有哪些共同好友，</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">wangwu</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#A6ACCD;">sinter zs ls</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 查询张三和李四的好友总共有哪些人</span></span>
<span class="line"><span style="color:#A6ACCD;">sunion zs ls</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 查询哪些人是张三的好友却不是李四的好友，</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">zhaoliu</span><span style="color:#89DDFF;">&quot;</span><span style="color:#A6ACCD;">，</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">lisi</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#A6ACCD;">sdiff zs ls</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 判断李四是否是张三的好友，</span><span style="color:#F78C6C;">1</span></span>
<span class="line"><span style="color:#A6ACCD;">sismember zs lisi</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 判断张三是否是李四的好友，</span><span style="color:#F78C6C;">0</span></span>
<span class="line"><span style="color:#A6ACCD;">sismember lsis zs</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 将李四从张三的好友列表中移除，</span><span style="color:#F78C6C;">1</span></span>
<span class="line"><span style="color:#A6ACCD;">srem zs lisi</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 再次查看张三好友，</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">zhaoliu</span><span style="color:#89DDFF;">&quot;</span><span style="color:#A6ACCD;">，</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">wangwu</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#A6ACCD;">smembers zs</span></span></code></pre></div><h3 id="应用场景-1" tabindex="-1">应用场景 <a class="header-anchor" href="#应用场景-1" aria-label="Permalink to &quot;应用场景&quot;">​</a></h3><blockquote><p>因此 Set 类型比较适合用来数据去重和保障数据的唯一性，还可以用来统计多个集合的交集、错集和并集等，当我们存储的数据是无序并且需要去重的情况下，比较适合使用集合类型进行存储。</p></blockquote><blockquote><p>但是要提醒你一下，这里有一个潜在的风险。<strong>Set 的差集、并集和交集的计算复杂度较高，在数据量较大的情况下，如果直接执行这些计算，会导致 Redis 实例阻塞</strong>。</p></blockquote><blockquote><p>在主从集群中，为了避免主库因为 Set 做聚合计算（交集、差集、并集）时导致主库被阻塞，<strong>我们可以选择一个从库完成聚合统计，或者把数据返回给客户端，由客户端来完成聚合统计。</strong></p></blockquote><h4 id="点赞" tabindex="-1">点赞 <a class="header-anchor" href="#点赞" aria-label="Permalink to &quot;点赞&quot;">​</a></h4><p>Set 类型可以保证一个用户只能点一个赞，这里举例子一个场景，key 是文章id，value 是用户id。</p><p><code>uid:1</code> 、<code>uid:2</code>、<code>uid:3</code> 三个用户分别对 article:1 文章点赞了。</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> uid:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> 用户对文章 article:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> 点赞</span></span>
<span class="line"><span style="color:#A6ACCD;">sadd article:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> uid:</span><span style="color:#F78C6C;">1</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> uid:</span><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;"> 用户对文章 article:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> 点赞</span></span>
<span class="line"><span style="color:#A6ACCD;">sadd article:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> uid:</span><span style="color:#F78C6C;">2</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> uid:</span><span style="color:#F78C6C;">3</span><span style="color:#A6ACCD;"> 用户对文章 article:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> 点赞</span></span>
<span class="line"><span style="color:#A6ACCD;">sadd article:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> uid:</span><span style="color:#F78C6C;">3</span></span></code></pre></div><p><code>uid:1</code> 取消了对 article:1 文章点赞。</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">srem article:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> uid:</span><span style="color:#F78C6C;">1</span></span></code></pre></div><p>获取 article:1 文章所有点赞用户 :</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">smembers article:</span><span style="color:#F78C6C;">1</span></span></code></pre></div><p>获取 article:1 文章的点赞用户数量：</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">scard article:</span><span style="color:#F78C6C;">1</span></span></code></pre></div><p>判断用户 <code>uid:1</code> 是否对文章 article:1 点赞了</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 返回</span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;">说明没点赞，返回</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;">则说明点赞了</span></span>
<span class="line"><span style="color:#A6ACCD;">sismember article:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> uid:</span><span style="color:#F78C6C;">1</span></span></code></pre></div><h4 id="共同关注" tabindex="-1">共同关注 <a class="header-anchor" href="#共同关注" aria-label="Permalink to &quot;共同关注&quot;">​</a></h4><blockquote><p>Set 类型支持交集运算，所以可以用来计算共同关注的好友、公众号等。key 可以是用户id，value 则是已关注的公众号的id。uid:1 用户关注公众号 id 为 5、6、7、8、9，uid:2用户关注公众号 id 为 7、8、9、10、11</p></blockquote><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> uid:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> 用户关注公众号 id 为 </span><span style="color:#F78C6C;">5</span><span style="color:#A6ACCD;">、</span><span style="color:#F78C6C;">6</span><span style="color:#A6ACCD;">、</span><span style="color:#F78C6C;">7</span><span style="color:#A6ACCD;">、</span><span style="color:#F78C6C;">8</span><span style="color:#A6ACCD;">、</span><span style="color:#F78C6C;">9</span></span>
<span class="line"><span style="color:#A6ACCD;">sadd uid:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">5</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">6</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">7</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">8</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">9</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> uid:</span><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;">  用户关注公众号 id 为 </span><span style="color:#F78C6C;">7</span><span style="color:#A6ACCD;">、</span><span style="color:#F78C6C;">8</span><span style="color:#A6ACCD;">、</span><span style="color:#F78C6C;">9</span><span style="color:#A6ACCD;">、</span><span style="color:#F78C6C;">10</span><span style="color:#A6ACCD;">、</span><span style="color:#F78C6C;">11</span></span>
<span class="line"><span style="color:#A6ACCD;">sadd uid:</span><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">7</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">8</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">9</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">10</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">11</span></span></code></pre></div><p><code>uid:1</code> 和 <code>uid:2</code> 共同关注的公众号：</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 获取共同关注</span></span>
<span class="line"><span style="color:#A6ACCD;">siner uid:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> uid:</span><span style="color:#F78C6C;">2</span></span></code></pre></div><p>给 <code>uid:2</code> 推荐 <code>uid:1</code> 关注的公众号：</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight has-diff"><code><span class="line"><span style="color:#A6ACCD;">sdiff uid:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> uid:</span><span style="color:#F78C6C;">2</span></span></code></pre></div><p>验证某个公众号是否同时被 <code>uid:1</code> 或 <code>uid:2</code> 关注:</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 返回</span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;">，说明没关注，返回</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;">，说明关注了</span></span>
<span class="line"><span style="color:#A6ACCD;">sismember uid:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">5</span></span>
<span class="line"><span style="color:#A6ACCD;">sismember uid:</span><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">5</span></span></code></pre></div><h4 id="抽奖活动" tabindex="-1">抽奖活动 <a class="header-anchor" href="#抽奖活动" aria-label="Permalink to &quot;抽奖活动&quot;">​</a></h4><blockquote><p>存储某活动中中奖的用户名 ，Set 类型因为有去重功能，可以保证同一个用户不会中奖两次。key为抽奖活动名，value为员工名称，把所有员工名称放入抽奖箱 ：</p></blockquote><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">sadd lucky Tom Jerry John Sean Marry Lindy Sary Mark</span></span></code></pre></div><p>如果允许重复中奖，可以使用 SRANDMEMBER 命令。</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 抽取 </span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> 个一等奖，抽取 </span><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;"> 个二等奖：</span></span>
<span class="line"><span style="color:#A6ACCD;">srandmember lucky </span><span style="color:#F78C6C;">1</span></span>
<span class="line"><span style="color:#A6ACCD;">srandmember lucky </span><span style="color:#F78C6C;">2</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 抽取 </span><span style="color:#F78C6C;">3</span><span style="color:#A6ACCD;"> 个三等奖</span></span>
<span class="line"><span style="color:#A6ACCD;">srandmember lucky </span><span style="color:#F78C6C;">3</span></span></code></pre></div><p>如果不允许重复中奖，可以使用 SPOP 命令。</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 抽取一等奖</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;">个</span><span style="color:#89DDFF;">,#</span><span style="color:#A6ACCD;"> 抽取二等奖</span><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;">个</span><span style="color:#89DDFF;">,#</span><span style="color:#A6ACCD;"> 抽取三等奖</span><span style="color:#F78C6C;">3</span><span style="color:#A6ACCD;">个</span></span>
<span class="line"><span style="color:#A6ACCD;">spop lucky </span><span style="color:#F78C6C;">1</span></span>
<span class="line"><span style="color:#A6ACCD;">spop lucky </span><span style="color:#F78C6C;">2</span></span>
<span class="line"><span style="color:#A6ACCD;">spop lucky </span><span style="color:#F78C6C;">3</span></span></code></pre></div><h3 id="数据结构-2" tabindex="-1">数据结构 <a class="header-anchor" href="#数据结构-2" aria-label="Permalink to &quot;数据结构&quot;">​</a></h3><blockquote><p>Set 类型的底层数据结构是由<strong>哈希表或整数集合</strong>实现的：</p><ul><li>如果集合中的元素都是整数且元素个数小于 <code>512</code> （默认值，<code>set-maxintset-entries</code>配置）个，Redis 会使用<strong>整数集合</strong>作为 Set 类型的底层数据结构；</li><li>如果集合中的元素不满足上面条件，则 Redis 使用<strong>哈希表</strong>作为 Set 类型的底层数据结构。</li></ul></blockquote><h2 id="有序集合zset" tabindex="-1">有序集合Zset <a class="header-anchor" href="#有序集合zset" aria-label="Permalink to &quot;有序集合Zset&quot;">​</a></h2><blockquote><p>Redis有序集合zset与普通集合set非常相似，是一个<strong>没有重复元素的字符串集合</strong>。不同之处是有序集合的每个成员都关联了一个<strong>评分（score）</strong>，<strong>这个评分（score）被用来按照从最低分到最高分的方式排序集合中的成员</strong>。集合的成员是唯一的，但是评分可以是重复了 。</p></blockquote><blockquote><p>因为元素是有序的, 所以你也可以很快的根据评分（score）或者次序（position）来获取一个范围的元素。访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。<strong>可排序、元素不重复、查询速度快、经常被用来实现排行榜这样的功能</strong>。</p></blockquote><h3 id="命令使用" tabindex="-1">命令使用 <a class="header-anchor" href="#命令使用" aria-label="Permalink to &quot;命令使用&quot;">​</a></h3><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301241113557.png" alt="image-20230124111301402" style="zoom:60%;"><div class="language-ASN.1"><button title="Copy Code" class="copy"></button><span class="lang">ASN.1</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">-- 将一个或多个member元素及其 score 值加入到有序集key当中</span></span>
<span class="line"><span style="color:#A6ACCD;">zadd &lt;key&gt; &lt;score1&gt;&lt;value1&gt; &lt;score2&gt;&lt;value2&gt;…</span></span>
<span class="line"><span style="color:#A6ACCD;">zadd topn 10 java 20 c++ 15 c 14 mysql 17 redis</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">-- 返回有序集key中，下标在&lt;start&gt;&lt;stop&gt;之间的元素带withscores，可以让分数一起和值返回到结果集</span></span>
<span class="line"><span style="color:#A6ACCD;">zrange &lt;key&gt; &lt;start&gt;&lt;stop&gt; [WITHSCORES]</span></span>
<span class="line"><span style="color:#A6ACCD;">zrange topn 0 -1</span></span>
<span class="line"><span style="color:#A6ACCD;">zrange topn 0 -1 withscores</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">-- 元素个数</span></span>
<span class="line"><span style="color:#A6ACCD;">zcard key</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">-- 返回有序集key中，所有score 值介于min和max之间(包括等于min或max)的成员。</span></span>
<span class="line"><span style="color:#A6ACCD;">-- 有序集成员按 score 值递增(从小到大)次序排列</span></span>
<span class="line"><span style="color:#A6ACCD;">zrangebyscore key minmax [withscores] [limit offset count]</span></span>
<span class="line"><span style="color:#A6ACCD;">zrangebyscore topn 10 15</span></span>
<span class="line"><span style="color:#A6ACCD;">zrangebyscore topn 10 15 withscores</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">-- 改为从大到小排列，排名默认升序，降序则在Z后面加上REV即可，如下</span></span>
<span class="line"><span style="color:#A6ACCD;">zrevrangebyscore key max min [withscores] [limit offset count]        </span></span>
<span class="line"><span style="color:#A6ACCD;">zrevrangebyscore topn 20 14</span></span>
<span class="line"><span style="color:#A6ACCD;">zrevrangebyscore topn 20 14 withscores</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">-- 为元素的score加上增量</span></span>
<span class="line"><span style="color:#A6ACCD;">zincrby &lt;key&gt; &lt;increment&gt; &lt;value&gt;   </span></span>
<span class="line"><span style="color:#A6ACCD;">zincrby topn 2 mysql</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">-- 删除该集合下，指定值的元素</span></span>
<span class="line"><span style="color:#A6ACCD;">zrem &lt;key&gt;&lt;value&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">zrem topn java</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">-- 统计该集合，分数区间内的元素个数 </span></span>
<span class="line"><span style="color:#A6ACCD;">zcount &lt;key&gt;&lt;min&gt;&lt;max&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">zcount topn 10 15</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">-- 返回该值在集合中的排名，从0开始</span></span>
<span class="line"><span style="color:#A6ACCD;">zrank &lt;key&gt;&lt;value&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">zrank topn mysql</span></span></code></pre></div><h3 id="实战案例" tabindex="-1">实战案例 <a class="header-anchor" href="#实战案例" aria-label="Permalink to &quot;实战案例&quot;">​</a></h3><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">-- 将班级的下列学生得分存入Redis的SortedSet中：并实现下列功能：</span></span>
<span class="line"><span style="color:#A6ACCD;">zadd stus </span><span style="color:#F78C6C;">85</span><span style="color:#A6ACCD;"> Jack </span><span style="color:#F78C6C;">89</span><span style="color:#A6ACCD;"> Lucy </span><span style="color:#F78C6C;">82</span><span style="color:#A6ACCD;"> Rose </span><span style="color:#F78C6C;">95</span><span style="color:#A6ACCD;"> Tom </span><span style="color:#F78C6C;">78</span><span style="color:#A6ACCD;"> Jerry </span><span style="color:#F78C6C;">92</span><span style="color:#A6ACCD;"> Amy </span><span style="color:#F78C6C;">76</span><span style="color:#A6ACCD;"> Miles</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 删除Tom同学</span></span>
<span class="line"><span style="color:#A6ACCD;">zrem stus Tom</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 获取Rose同学的排名:2</span></span>
<span class="line"><span style="color:#A6ACCD;">zrank stus Rose</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 查询80分以下有几个学生:2</span></span>
<span class="line"><span style="color:#A6ACCD;">zcount stus </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">80</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 给Amy同学加2分</span></span>
<span class="line"><span style="color:#A6ACCD;">zincrby stus </span><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;"> Amy</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 查出成绩前3名的同学</span></span>
<span class="line"><span style="color:#A6ACCD;">zrevrange stus </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">2</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 查出成绩80分以下的所有同学</span></span>
<span class="line"><span style="color:#A6ACCD;">zrangebyscore stus </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">80</span></span></code></pre></div><h3 id="应用场景-2" tabindex="-1"><strong>应用场景</strong> <a class="header-anchor" href="#应用场景-2" aria-label="Permalink to &quot;**应用场景**&quot;">​</a></h3><blockquote><p>Zset 类型（Sorted Set，有序集合） 可以根据元素的权重来排序，我们可以自己来决定每个元素的权重值。比如说，我们可以根据元素插入 Sorted Set 的时间确定权重值，先插入的元素权重小，后插入的元素权重大。</p><p>在面对需要展示最新列表、排行榜等场景时，如果数据更新频繁或者需要分页显示，可以优先考虑使用 Sorted Set。</p></blockquote><h4 id="排行榜-1" tabindex="-1">排行榜 <a class="header-anchor" href="#排行榜-1" aria-label="Permalink to &quot;排行榜&quot;">​</a></h4><p>有序集合比较典型的使用场景就是排行榜。例如学生成绩的排名榜、游戏积分排行榜、视频播放排名、电商系统中商品的销量排名等。我们以博文点赞排名为例，小林发表了五篇博文，分别获得赞为 200、40、100、50、150。</p><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">zadd </span><span style="color:#C792EA;">&lt;key&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;score1&gt;&lt;value1&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;score2&gt;&lt;value2&gt;</span><span style="color:#A6ACCD;">…</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">arcticle</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> 文章获得了200个赞</span></span>
<span class="line"><span style="color:#A6ACCD;">zadd </span><span style="color:#FFCB6B;">user</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">xiaolin</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">ranking</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">200</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">arcticle</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">1</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">arcticle</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;"> 文章获得了40个赞</span></span>
<span class="line"><span style="color:#A6ACCD;">zadd </span><span style="color:#FFCB6B;">user</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">xiaolin</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">ranking</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">40</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">arcticle</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">2</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">arcticle</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">3</span><span style="color:#A6ACCD;"> 文章获得了100个赞</span></span>
<span class="line"><span style="color:#A6ACCD;">zadd </span><span style="color:#FFCB6B;">user</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">xiaolin</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">ranking</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">100</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">arcticle</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">3</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">arcticle</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">4</span><span style="color:#A6ACCD;"> 文章获得了50个赞</span></span>
<span class="line"><span style="color:#A6ACCD;">zadd </span><span style="color:#FFCB6B;">user</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">xiaolin</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">ranking</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">50</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">arcticle</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">4</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">arcticle</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">5</span><span style="color:#A6ACCD;"> 文章获得了150个赞</span></span>
<span class="line"><span style="color:#A6ACCD;">zadd </span><span style="color:#FFCB6B;">user</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">xiaolin</span><span style="color:#A6ACCD;">:</span><span style="color:#FFCB6B;">ranking</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">150</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">arcticle</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">5</span></span></code></pre></div><p>文章 arcticle:4 新增一个赞，可以使用 ZINCRBY 命令（为有序集合key中元素member的分值加上increment）：</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">zincrby user:xiaolin:ranking </span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> arcticle:</span><span style="color:#F78C6C;">4</span></span>
<span class="line"><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">51</span><span style="color:#89DDFF;">&quot;</span></span></code></pre></div><p>查看某篇文章的赞数，可以使用 ZSCORE 命令（返回有序集合key中元素个数）：</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">zscore user:xiaolin:ranking arcticle:</span><span style="color:#F78C6C;">4</span></span>
<span class="line"><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">50</span><span style="color:#89DDFF;">&quot;</span></span></code></pre></div><p>获取小林文章赞数最多的 3 篇文章，可以使用 ZREVRANGE ，倒序获取有序集合 key 从start下标到stop下标的元素</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;"># WITHSCORES 表示把 score 也显示出来</span></span>
<span class="line"><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> zrevrange user:xiaolin:ranking </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;"> withscores</span></span>
<span class="line"><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">arcticle:1</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">200</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">3</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">arcticle:5</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">4</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">150</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">5</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">arcticle:3</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">6</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">100</span><span style="color:#89DDFF;">&quot;</span></span></code></pre></div><p>获取小林 100 赞到 200 赞的文章，可以使用 ZRANGEBYSCORE 命令（返回有序集合中指定分数区间内的成员，分数由低到高排序）：</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> zrangebyscore user:xiaolin:ranking </span><span style="color:#F78C6C;">100</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">200</span><span style="color:#A6ACCD;"> withscores</span></span>
<span class="line"><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">arcticle:3</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">100</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">3</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">arcticle:5</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">4</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">150</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">5</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">arcticle:1</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">6</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">200</span><span style="color:#89DDFF;">&quot;</span></span></code></pre></div><h4 id="电话、姓名排序" tabindex="-1">电话、姓名排序 <a class="header-anchor" href="#电话、姓名排序" aria-label="Permalink to &quot;电话、姓名排序&quot;">​</a></h4><p>使用有序集合的 <code>ZRANGEBYLEX</code> 或 <code>ZREVRANGEBYLEX</code> 可以帮助我们实现电话号码或姓名的排序，我们以 <code>ZRANGEBYLEX</code> （返回指定成员区间内的成员，按 key 正序排列，分数必须相同）为例。</p><p><strong>注意：不要在分数不一致的 SortSet 集合中去使用 ZRANGEBYLEX和 ZREVRANGEBYLEX 指令，因为获取的结果会不准确。</strong></p><p><em>1、电话排序</em></p><p>我们可以将电话号码存储到 SortSet 中，然后根据需要来获取号段：</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> ZADD phone </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">13100111100</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">13110114300</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">13132110901</span><span style="color:#A6ACCD;"> </span></span>
<span class="line"><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">integer</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">3</span></span>
<span class="line"><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> ZADD phone </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">13200111100</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">13210414300</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">13252110901</span><span style="color:#A6ACCD;"> </span></span>
<span class="line"><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">integer</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">3</span></span>
<span class="line"><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> ZADD phone </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">13300111100</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">13310414300</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">13352110901</span><span style="color:#A6ACCD;"> </span></span>
<span class="line"><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">integer</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">3</span></span></code></pre></div><p>获取所有号码:</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> ZRANGEBYLEX phone </span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">+</span></span>
<span class="line"><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">13100111100</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">13110114300</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">3</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">13132110901</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">4</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">13200111100</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">5</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">13210414300</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">6</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">13252110901</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">7</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">13300111100</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">8</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">13310414300</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">9</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">13352110901</span><span style="color:#89DDFF;">&quot;</span></span></code></pre></div><p>获取 132 号段的号码：</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> ZRANGEBYLEX phone </span><span style="color:#89DDFF;">[</span><span style="color:#F78C6C;">132</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">(</span><span style="color:#F78C6C;">133</span></span>
<span class="line"><span style="color:#F78C6C;">1</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">13200111100</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">13210414300</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">3</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">13252110901</span><span style="color:#89DDFF;">&quot;</span></span></code></pre></div><p>获取132、133号段的号码：</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> ZRANGEBYLEX phone </span><span style="color:#89DDFF;">[</span><span style="color:#F78C6C;">132</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">(</span><span style="color:#F78C6C;">134</span></span>
<span class="line"><span style="color:#F78C6C;">1</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">13200111100</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">13210414300</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">3</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">13252110901</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">4</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">13300111100</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">5</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">13310414300</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">6</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">13352110901</span><span style="color:#89DDFF;">&quot;</span></span></code></pre></div><p>2、姓名排序</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> zadd names </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> Toumas </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> Jake </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> Bluetuo </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> Gaodeng </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> Aimini </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> Aidehua </span></span>
<span class="line"><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">integer</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">6</span></span></code></pre></div><p>获取所有人的名字:</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> ZRANGEBYLEX names </span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">+</span></span>
<span class="line"><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">Aidehua</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">Aimini</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">3</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">Bluetuo</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">4</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">Gaodeng</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">5</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">Jake</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">6</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">Toumas</span><span style="color:#89DDFF;">&quot;</span></span></code></pre></div><p>获取名字中大写字母A开头的所有人：</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> ZRANGEBYLEX names </span><span style="color:#89DDFF;">[</span><span style="color:#82AAFF;">A</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">B</span></span>
<span class="line"><span style="color:#F78C6C;">1</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">Aidehua</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">Aimini</span><span style="color:#89DDFF;">&quot;</span></span></code></pre></div><p>获取名字中大写字母 C 到 Z 的所有人：</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> ZRANGEBYLEX names </span><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">C </span><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">Z</span></span>
<span class="line"><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">Gaodeng</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">Jake</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#F78C6C;">3</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">Toumas</span><span style="color:#89DDFF;">&quot;</span></span></code></pre></div><h4 id="班级分数" tabindex="-1">班级分数 <a class="header-anchor" href="#班级分数" aria-label="Permalink to &quot;班级分数&quot;">​</a></h4><p>将班级的下列学生得分存入Redis的SortedSet中：</p><p>Jack 85, Lucy 89, Rose 82, Tom 95, Jerry 78, Amy 92, Miles 76</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">zadd stus </span><span style="color:#F78C6C;">85</span><span style="color:#A6ACCD;"> Jack  </span><span style="color:#F78C6C;">89</span><span style="color:#A6ACCD;"> Lucy  </span><span style="color:#F78C6C;">82</span><span style="color:#A6ACCD;"> Rose </span><span style="color:#F78C6C;">95</span><span style="color:#A6ACCD;"> Tom </span><span style="color:#F78C6C;">78</span><span style="color:#A6ACCD;"> Jerry </span><span style="color:#F78C6C;">92</span><span style="color:#A6ACCD;"> Amy </span><span style="color:#F78C6C;">76</span><span style="color:#A6ACCD;"> Miles</span></span></code></pre></div><p>并实现下列功能：</p><ul><li>删除Tom同学</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1</span></span>
<span class="line"><span style="color:#A6ACCD;">zrem stus Tom</span></span></code></pre></div><ul><li>获取Amy同学的分数</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">zscore  stus  Amy</span></span></code></pre></div><ul><li>获取Rose同学的排名</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">zrank stus  Rose</span></span></code></pre></div><ul><li>查询80分以下有几个学生</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">zcount  stus  </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;">  </span><span style="color:#F78C6C;">80</span></span></code></pre></div><ul><li>给Amy同学加2分</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">zincrby stus </span><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;">  Amy</span></span></code></pre></div><ul><li>查出成绩前3名的同学</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">zrange stus </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;">  </span><span style="color:#F78C6C;">2</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 后三名同学</span></span>
<span class="line"><span style="color:#A6ACCD;">zrevrange  stus  </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">2</span></span></code></pre></div><ul><li>查出成绩80分以下的所有同学</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">zrangebyscore stus  </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;">  </span><span style="color:#F78C6C;">80</span></span></code></pre></div><h3 id="数据结构-3" tabindex="-1">数据结构 <a class="header-anchor" href="#数据结构-3" aria-label="Permalink to &quot;数据结构&quot;">​</a></h3><h4 id="基本数据结构" tabindex="-1">基本数据结构 <a class="header-anchor" href="#基本数据结构" aria-label="Permalink to &quot;基本数据结构&quot;">​</a></h4><blockquote><p>SortedSet(zset)是Redis提供的一个非常特别的数据结构，一方面它等价于Java的数据结构Map&lt;String, Double&gt;，可以给每一个元素value赋予一个权重score，另一方面它又类似于TreeSet，内部的元素会按照权重score进行排序，可以得到每个元素的名次，还可以通过score的范围来获取元素的列表。</p></blockquote><p>zset底层使用了两个数据结构</p><blockquote><p>hash，hash的作用是关联元素value和权重score，保障元素value的唯一性，可以通过元素value找到相应的score值</p><p>跳跃表，跳跃表的目的在于给元素value排序，根据score的范围获取元素列表。</p></blockquote><h4 id="跳跃表" tabindex="-1">跳跃表 <a class="header-anchor" href="#跳跃表" aria-label="Permalink to &quot;跳跃表&quot;">​</a></h4><blockquote><p>有序集合在生活中比较常见，例如根据成绩对学生排名，根据得分对玩家排名等。对于有序集合的底层实现，可以用数组、平衡树、链表等。数组不便元素的插入、删除；平衡树或红黑树虽然效率高但结构复杂；链表查询需要遍历所有效率低。Redis采用的是跳跃表。跳跃表效率堪比红黑树，实现远比红黑树简单。</p></blockquote><p>对比有序链表和跳跃表，从链表中查询出51</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301251054831.png" alt="image-20230125105446724" style="zoom:67%;"><p>要查找值为51的元素，需要从第一个元素开始依次查找、比较才能找到。共需要6次比较。</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301251055059.png" alt="image-20230125105512942" style="zoom:67%;"><p>从第2层开始，1节点比51节点小，向后比较。</p><p>21节点比51节点小，继续向后比较，后面就是NULL了，所以从21节点向下到第1层</p><p>在第1层，41节点比51节点小，继续向后，61节点比51节点大，所以从41向下</p><p>在第0层，51节点为要查找的节点，节点被找到，共查找4次。</p><p>从此可以看出跳跃表比有序链表效率要高</p><h2 id="哈希-hash" tabindex="-1">哈希(Hash) <a class="header-anchor" href="#哈希-hash" aria-label="Permalink to &quot;哈希(Hash)&quot;">​</a></h2><blockquote><p>Hash类型，也叫散列，其value是一个无序字典，<strong>类似于Java中的HashMap结构</strong>。<strong>Hash类型特别适用于存储对象，方便修改对象的某个属性</strong></p></blockquote><h3 id="string-hash-结构" tabindex="-1">String &amp; Hash 结构 <a class="header-anchor" href="#string-hash-结构" aria-label="Permalink to &quot;String &amp; Hash 结构&quot;">​</a></h3><blockquote><p><strong>String结构是将对象序列化为JSON字符串后存储</strong>，<strong>当需要修改对象某个字段时很不方便</strong></p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.5/202205042054477.png" alt="image-20220504205408418" style="zoom:80%;"><blockquote><p><strong>Hash结构可以将对象中的每个字段独立存储，可以针对单个字段做CRUD</strong></p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.5/202205042054772.png" alt="image-20220504205457710" style="zoom:80%;"><h3 id="命令使用-1" tabindex="-1">命令使用 <a class="header-anchor" href="#命令使用-1" aria-label="Permalink to &quot;命令使用&quot;">​</a></h3><blockquote><p>本质上就是在String命令上加了个h前缀</p></blockquote><h4 id="增加查询" tabindex="-1">增加查询 <a class="header-anchor" href="#增加查询" aria-label="Permalink to &quot;增加查询&quot;">​</a></h4><div class="language-sql"><button title="Copy Code" class="copy"></button><span class="lang">sql</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">-- 给&lt;key&gt;集合中&lt;field&gt;键赋值&lt;value&gt;，添加或者修改hash类型key的field的值</span></span>
<span class="line"><span style="color:#A6ACCD;">hset </span><span style="color:#F78C6C;">key</span><span style="color:#A6ACCD;"> field </span><span style="color:#F78C6C;">value</span></span>
<span class="line"><span style="color:#A6ACCD;">hset user:</span><span style="color:#F78C6C;">1001</span><span style="color:#A6ACCD;"> id </span><span style="color:#F78C6C;">1</span></span>
<span class="line"><span style="color:#A6ACCD;">hset user:</span><span style="color:#F78C6C;">1001</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">name</span><span style="color:#A6ACCD;"> zhangsan</span></span>
<span class="line"><span style="color:#A6ACCD;">hset user:</span><span style="color:#F78C6C;">1001</span><span style="color:#A6ACCD;"> age </span><span style="color:#F78C6C;">33</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 修改的时候会直接进行覆盖</span></span>
<span class="line"><span style="color:#A6ACCD;">hset user:</span><span style="color:#F78C6C;">1001</span><span style="color:#A6ACCD;"> age </span><span style="color:#F78C6C;">55</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 批量存储</span></span>
<span class="line"><span style="color:#A6ACCD;">hmset user:</span><span style="color:#F78C6C;">1002</span><span style="color:#A6ACCD;"> id </span><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">name</span><span style="color:#A6ACCD;"> lisi port </span><span style="color:#F78C6C;">1003</span><span style="color:#A6ACCD;"> school TUT</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 从&lt;key1&gt;集合&lt;field&gt;取出 value，获取一个hash类型key的field的值</span></span>
<span class="line"><span style="color:#A6ACCD;">hget user:</span><span style="color:#F78C6C;">1001</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">name</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 批量获取多个hash类型key的field的值</span></span>
<span class="line"><span style="color:#A6ACCD;">hmget user:</span><span style="color:#F78C6C;">1002</span><span style="color:#A6ACCD;"> id </span><span style="color:#F78C6C;">name</span><span style="color:#A6ACCD;"> school</span></span></code></pre></div><h4 id="批量获取键值" tabindex="-1">批量获取键值 <a class="header-anchor" href="#批量获取键值" aria-label="Permalink to &quot;批量获取键值&quot;">​</a></h4><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">-- 获取一个hash类型的key中的所有的field和value</span></span>
<span class="line"><span style="color:#A6ACCD;">hgetall </span><span style="color:#FFCB6B;">user</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">1002</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 列出该hash集合的所有field</span></span>
<span class="line"><span style="color:#A6ACCD;">hkeys </span><span style="color:#FFCB6B;">user</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">1002</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 列出该hash集合的所有value，获取一个hash类型的key中的所有的value</span></span>
<span class="line"><span style="color:#A6ACCD;">hvals </span><span style="color:#FFCB6B;">user</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">1002</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 查看哈希表 key 中，给定域 field 是否存在</span></span>
<span class="line"><span style="color:#A6ACCD;">hexists </span><span style="color:#FFCB6B;">user</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">1002</span><span style="color:#A6ACCD;"> name</span></span></code></pre></div><h4 id="自增自减-1" tabindex="-1">自增自减 <a class="header-anchor" href="#自增自减-1" aria-label="Permalink to &quot;自增自减&quot;">​</a></h4><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">-- 为哈希表 key 中的域 field 的值加上增量,让一个hash类型key的字段值自增并指定步长</span></span>
<span class="line"><span style="color:#A6ACCD;">hincrby </span><span style="color:#C792EA;">&lt;key&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;field&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;increment&gt;</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 给id增加2</span></span>
<span class="line"><span style="color:#A6ACCD;">hincrby </span><span style="color:#FFCB6B;">user</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">1002</span><span style="color:#A6ACCD;"> id </span><span style="color:#F78C6C;">2</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 给id自减2</span></span>
<span class="line"><span style="color:#A6ACCD;">hincrby </span><span style="color:#FFCB6B;">user</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">1002</span><span style="color:#A6ACCD;"> id </span><span style="color:#89DDFF;">-</span><span style="color:#F78C6C;">2</span></span></code></pre></div><h4 id="组合命令-1" tabindex="-1">组合命令 <a class="header-anchor" href="#组合命令-1" aria-label="Permalink to &quot;组合命令&quot;">​</a></h4><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">-- 添加一个hash类型的key的field值，前提是这个field不存在，否则不执行</span></span>
<span class="line"><span style="color:#A6ACCD;">hsetnx </span><span style="color:#C792EA;">&lt;key&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;field&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">&lt;value&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">hsetnx </span><span style="color:#FFCB6B;">user</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">1002</span><span style="color:#A6ACCD;"> school TJUT</span></span></code></pre></div><h3 id="应用场景-3" tabindex="-1">应用场景 <a class="header-anchor" href="#应用场景-3" aria-label="Permalink to &quot;应用场景&quot;">​</a></h3><h4 id="缓存对象-1" tabindex="-1">缓存对象 <a class="header-anchor" href="#缓存对象-1" aria-label="Permalink to &quot;缓存对象&quot;">​</a></h4><p>Hash 类型的 （key，field， value） 的结构与对象的（对象id， 属性， 值）的结构相似，也可以用来存储对象。</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301251139299.png" alt="image-20230125113902191" style="zoom:60%;"><p>我们可以使用如下命令，将用户对象的信息存储到 Hash 类型：</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 存储一个哈希表uid:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;">的键值</span></span>
<span class="line"><span style="color:#A6ACCD;">hset uid:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> name Tom age </span><span style="color:#F78C6C;">15</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 存储一个哈希表uid:</span><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;">的键值</span></span>
<span class="line"><span style="color:#A6ACCD;">hset uid:</span><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;"> name Jerry age </span><span style="color:#F78C6C;">13</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 获取哈希表用户id为</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;">中所有的键值</span></span>
<span class="line"><span style="color:#A6ACCD;">hgetall uid:</span><span style="color:#F78C6C;">1</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301251138888.png" alt="image-20230125113828741" style="zoom:67%;"><h4 id="购物车" tabindex="-1">购物车 <a class="header-anchor" href="#购物车" aria-label="Permalink to &quot;购物车&quot;">​</a></h4><p>以用户 id 为 key，商品 id 为 field，商品数量为 value，恰好构成了购物车的3个要素，如下图所示。</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.11/202206182221093.png" alt="image-20220618222142941" style="zoom:50%;"><p>涉及的命令如下：</p><ul><li>添加商品：<code>HSET cart:{用户id} {商品id} 1</code></li><li>添加数量：<code>HINCRBY cart:{用户id} {商品id} 1</code></li><li>商品总数：<code>HLEN cart:{用户id}</code></li><li>删除商品：<code>HDEL cart:{用户id} {商品id}</code></li><li>获取购物车所有商品：<code>HGETALL cart:{用户id}</code></li></ul><p>当前仅仅是将商品ID存储到了Redis 中，在回显商品具体信息的时候，还需要拿着商品 id 查询一次数据库，获取完整的商品的信息</p><h3 id="数据结构-4" tabindex="-1">数据结构 <a class="header-anchor" href="#数据结构-4" aria-label="Permalink to &quot;数据结构&quot;">​</a></h3><p>Hash 类型的底层数据结构是由<strong>压缩列表或哈希表</strong>实现的：</p><ul><li>如果哈希类型元素个数小于 <code>512</code> 个（默认值，可由 <code>hash-max-ziplist-entries</code> 配置），所有值小于 <code>64</code> 字节（默认值，可由 <code>hash-max-ziplist-value</code> 配置）的话，Redis 会使用<strong>压缩列表</strong>作为 Hash 类型的底层数据结构；</li><li>如果哈希类型元素不满足上面条件，Redis 会使用<strong>哈希表</strong>作为 Hash 类型的 底层数据结构。</li></ul><p><strong>在 Redis 7.0 中，压缩列表数据结构已经废弃了，交由 listpack 数据结构来实现了</strong>。</p><h1 id="新数据类型⭐" tabindex="-1">新数据类型⭐ <a class="header-anchor" href="#新数据类型⭐" aria-label="Permalink to &quot;新数据类型⭐&quot;">​</a></h1><h2 id="bitmaps-位操作" tabindex="-1">Bitmaps 位操作 <a class="header-anchor" href="#bitmaps-位操作" aria-label="Permalink to &quot;Bitmaps 位操作&quot;">​</a></h2><blockquote><p>Redis提供了Bitmaps这个“数据类型”可以实现对位的操作：Bitmaps本身不是一种数据类型， 实际上它就是字符串（key-value） ， 但是它可以对字符串的位进行操作。</p></blockquote><blockquote><p>Bitmaps单独提供了一套命令， 所以在Redis中使用Bitmaps和使用字符串的方法不太相同。 <strong>可以把Bitmaps想象成一个以位为单位的数组， 数组的每个单元只能存储0和1， 数组的下标在Bitmaps中叫做偏移量。</strong></p></blockquote><blockquote><p>我们按月来统计用户签到信息，签到记录为1，未签到则记录为0，把每一个bit位对应当月的每一天，形成了映射关系。用0和1标示业务状态，这种思路就称为位图（BitMap）。Redis中是利用string类型数据结构实现BitMap，因此最大上限是512M，转换为bit则是 2^32个bit位。</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img/image-20220316110556705.png" alt="image-20220316110556705" style="zoom:80%;"><blockquote><p>什么是二值统计</p><p>也就是集合中的元素的值只有 0 和 1 两种，在签到打卡和用户是否登陆的场景中，只需记录<code>签到(1)</code>或 <code>未签到(0)</code>，<code>已登录(1)</code>或<code>未登陆(0)</code>。</p></blockquote><h3 id="内部实现" tabindex="-1">内部实现 <a class="header-anchor" href="#内部实现" aria-label="Permalink to &quot;内部实现&quot;">​</a></h3><blockquote><p>Bitmap 本身是用 String 类型作为底层数据结构实现的一种统计二值状态的数据类型。</p><p>String 类型是会保存为二进制的字节数组，所以，Redis 就把字节数组的每个 bit 位利用起来，用来表示一个元素的二值状态，你可以把 Bitmap 看作是一个 bit 数组。</p></blockquote><h3 id="常用命令-1" tabindex="-1">常用命令 <a class="header-anchor" href="#常用命令-1" aria-label="Permalink to &quot;常用命令&quot;">​</a></h3><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 设置值，其中value只能是 </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> 和 </span><span style="color:#F78C6C;">1</span></span>
<span class="line"><span style="color:#A6ACCD;">setbit key offset value</span></span>
<span class="line"><span style="color:#A6ACCD;">setbit users:</span><span style="color:#F78C6C;">20210101</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1</span></span>
<span class="line"><span style="color:#A6ACCD;">setbit users:</span><span style="color:#F78C6C;">20210101</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">6</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1</span></span>
<span class="line"><span style="color:#A6ACCD;">setbit users:</span><span style="color:#F78C6C;">20210102</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">11</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1</span></span>
<span class="line"><span style="color:#A6ACCD;">setbit users:</span><span style="color:#F78C6C;">20210102</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">13</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 获取值</span></span>
<span class="line"><span style="color:#A6ACCD;">getbit key offset</span></span>
<span class="line"><span style="color:#A6ACCD;">getbit users:</span><span style="color:#F78C6C;">20210101</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">13</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 获取指定范围内值为 </span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> 的个数，start 和 end 以字节为单位</span></span>
<span class="line"><span style="color:#A6ACCD;">bitcount key start end</span></span>
<span class="line"><span style="color:#A6ACCD;">bitcount users:</span><span style="color:#F78C6C;">20210101</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">-</span><span style="color:#F78C6C;">1</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> bitop 运算，bitop是一个复合操作</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 它可以做多个Bitmaps的and（交集） 、 or（并集） 、 not（非） 、 xor（异或） </span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 操作并将结果保存在destkey中</span></span>
<span class="line"><span style="color:#A6ACCD;">bitop and</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">or</span><span style="color:#89DDFF;">/</span><span style="color:#A6ACCD;">not</span><span style="color:#89DDFF;">/</span><span style="color:#A6ACCD;">xor</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&lt;</span><span style="color:#A6ACCD;">destkey</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">key1 key2…</span><span style="color:#89DDFF;">]</span></span>
<span class="line"><span style="color:#A6ACCD;">bitop and desk1 users:</span><span style="color:#F78C6C;">20210101</span><span style="color:#A6ACCD;"> users:</span><span style="color:#F78C6C;">20210102</span></span></code></pre></div><h3 id="实战演练" tabindex="-1">实战演练 <a class="header-anchor" href="#实战演练" aria-label="Permalink to &quot;实战演练&quot;">​</a></h3><p>2020-11-04 日访问网站的userid=1,2,5,9。</p><div class="language-assembly"><button title="Copy Code" class="copy"></button><span class="lang">assembly</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">setbit unique:users:20201104 1 1</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">setbit unique:users:20201104 2 1</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">setbit unique:users:20201104 5 1</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">setbit unique:users:20201104 9 1</span></span></code></pre></div><p>2020-11-03 日访问网站的userid=0,1,4,9</p><div class="language-assembly"><button title="Copy Code" class="copy"></button><span class="lang">assembly</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">setbit unique:users:20201103 0 1</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">setbit unique:users:20201103 1 1</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">setbit unique:users:20201103 4 1</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">setbit unique:users:20201103 9 1</span></span></code></pre></div><p>计算出两天都访问过网站的<strong>用户数量</strong>，结果为2，有两天是都访问过</p><div class="language-assembly"><button title="Copy Code" class="copy"></button><span class="lang">assembly</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">bitop and desk1 unique:users:20201103 unique:users:20201104</span></span>
<span class="line"><span style="color:#A6ACCD;">bitcount desk1</span></span></code></pre></div><p>计算出任意一天都访问过网站的<strong>用户数量</strong>（例如月活跃就是类似这种） ， 可以使用or求<strong>并集(去重)</strong>，结果为6</p><div class="language-assembly"><button title="Copy Code" class="copy"></button><span class="lang">assembly</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">bitop or desk2 unique:users:20201103 unique:users:20201104</span></span>
<span class="line"><span style="color:#A6ACCD;">bitcount desk2</span></span></code></pre></div><h3 id="应用场景-4" tabindex="-1"><strong>应用场景</strong> <a class="header-anchor" href="#应用场景-4" aria-label="Permalink to &quot;**应用场景**&quot;">​</a></h3><blockquote><p>Bitmap 类型非常适合二值状态统计的场景，这里的二值状态就是指集合元素的取值就只有 0 和 1 两种，在记录海量数据时，Bitmap 能够有效地节省内存空间。</p></blockquote><h4 id="用户签到统计⭐" tabindex="-1">用户签到统计⭐ <a class="header-anchor" href="#用户签到统计⭐" aria-label="Permalink to &quot;用户签到统计⭐&quot;">​</a></h4><p>在签到打卡的场景中，我们只用记录签到（1）或未签到（0），所以它就是非常典型的二值状态。</p><blockquote><p>签到统计时，每个用户一天的签到用 1 个 bit 位就能表示，一个月（假设是 31 天）的签到情况用 31 个 bit 位就可以，而一年的签到也只需要用 365 个 bit 位，根本不用太复杂的集合类型。</p></blockquote><p>比如统计编号 89757 的用户在 2021 年 5 月份的打卡情况要如何进行？</p><blockquote><p>key 可以设计成 <code>uid:sign:{userId}:{yyyyMM}</code>，月份的每一天的值 - 1 可以作为 offset（因为 offset 从 0 开始，所以 <code>offset = 日期 - 1</code>）。</p></blockquote><p>假设我们要统计 ID 100 的用户在 2022 年 6 月份的签到情况，就可以按照下面的步骤进行操作。</p><p>第一步，执行下面的命令，记录该用户 6 月 3 号已签到。</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">SETBIT uid:sign:</span><span style="color:#F78C6C;">100</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">202206</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1</span></span></code></pre></div><p>第二步，检查该用户 6 月 3 日是否签到。</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">GETBIT uid:sign:</span><span style="color:#F78C6C;">100</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">202206</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">2</span></span></code></pre></div><p>第三步，统计该用户在 6 月份的签到次数。</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">BITCOUNT uid:sign:</span><span style="color:#F78C6C;">100</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">202206</span></span></code></pre></div><p>这样，我们就知道该用户在 6 月份的签到情况了。</p><blockquote><p>如何统计这个月首次打卡时间呢？</p></blockquote><p>Redis 提供了 <code>BITPOS key bitValue [start] [end]</code>指令，返回数据表示 Bitmap 中第一个值为 <code>bitValue</code> 的 offset 位置。</p><p>在默认情况下， 命令将检测整个位图， 用户可以通过可选的 <code>start</code> 参数和 <code>end</code> 参数指定要检测的范围。所以我们可以通过执行这条命令来获取 userID = 100 在 2022 年 6 月份<strong>首次打卡</strong>日期：</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">BITPOS uid:sign:</span><span style="color:#F78C6C;">100</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">202206</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1</span></span></code></pre></div><blockquote><p>需要注意的是，因为 offset 从 0 开始的，所以我们需要将返回的 value + 1 。</p></blockquote><h4 id="判断用户登陆状态⭐" tabindex="-1">判断用户登陆状态⭐ <a class="header-anchor" href="#判断用户登陆状态⭐" aria-label="Permalink to &quot;判断用户登陆状态⭐&quot;">​</a></h4><blockquote><p>怎么用 Bitmap 来判断海量用户中某个用户是否在线呢？</p></blockquote><p>Bitmap 提供了 <code>GETBIT、SETBIT</code> 操作，通过一个偏移值 offset 对 bit 数组的 offset 位置的 bit 位进行读写操作，需要注意的是 offset 从 0 开始。</p><blockquote><p>只需要一个 key = login_status 表示存储用户登陆状态集合数据， 将用户 ID 作为 offset，在线就设置为 1，下线设置 0。通过 <code>GETBIT</code>判断对应的用户是否在线。50000 万 用户只需要 6 MB 的空间。</p></blockquote><p>假如我们要判断 ID = 10086 的用户的登陆情况：</p><p>第一步，执行以下指令，表示用户已登录。</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">SETBIT login_status </span><span style="color:#F78C6C;">10086</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1</span></span></code></pre></div><p>第二步，检查该用户是否登陆，返回值 1 表示已登录。</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">GETBIT login_status </span><span style="color:#F78C6C;">10086</span></span></code></pre></div><p>第三步，登出，将 offset 对应的 value 设置成 0。</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">SETBIT login_status </span><span style="color:#F78C6C;">10086</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span></span></code></pre></div><h4 id="连续签到用户总数" tabindex="-1">连续签到用户总数 <a class="header-anchor" href="#连续签到用户总数" aria-label="Permalink to &quot;连续签到用户总数&quot;">​</a></h4><blockquote><p>在记录了一个亿的用户连续 7 天的打卡数据，如何统计出这连续 7 天连续打卡用户总数呢？</p></blockquote><p>我们把每天的日期作为 Bitmap 的 key，userId 作为 offset，若是打卡则将 offset 位置的 bit 设置成 1。</p><p>key 对应的集合的每个 bit 位的数据则是一个用户在该日期的打卡记录。</p><p>一共有 7 个这样的 Bitmap，如果我们能对这 7 个 Bitmap 的对应的 bit 位做 『与』运算。同样的 UserID offset 都是一样的，当一个 userID 在 7 个 Bitmap 对应对应的 offset 位置的 bit = 1 就说明该用户 7 天连续打卡。</p><p>结果保存到一个新 Bitmap 中，我们再通过 <code>BITCOUNT</code> 统计 bit = 1 的个数便得到了连续打卡 3 天的用户总数了。</p><p>Redis 提供了 <code>BITOP operation destkey key [key ...]</code>这个指令用于对一个或者多个 key 的 Bitmap 进行位元操作。</p><p><code>opration</code> 可以是 <code>and</code>、<code>OR</code>、<code>NOT</code>、<code>XOR</code>。当 BITOP 处理不同长度的字符串时，较短的那个字符串所缺少的部分会被看作 <code>0</code> 。空的 <code>key</code> 也被看作是包含 <code>0</code> 的字符串序列。</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.8.30/202208131823521.png" alt="image-20220813182345432" style="zoom:50%;"><p>3 个 Bitmap，对应的 bit 位做「与」操作，结果保存到新的 Bitmap 中。</p><p>操作指令表示将 三个 bitmap 进行 AND 操作，并将结果保存到 destmap 中。接着对 destmap 执行 BITCOUNT 统计。</p><p>举个例子，比如将三个 bitmap 进行 AND 操作，并将结果保存到 destmap 中，接着对 destmap 执行 BITCOUNT 统计。</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 与操作</span></span>
<span class="line"><span style="color:#A6ACCD;">BITOP AND destmap bitmap:</span><span style="color:#F78C6C;">01</span><span style="color:#A6ACCD;"> bitmap:</span><span style="color:#F78C6C;">02</span><span style="color:#A6ACCD;"> bitmap:</span><span style="color:#F78C6C;">03</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 统计 bit 位 </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;">  </span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> 的个数</span></span>
<span class="line"><span style="color:#A6ACCD;">BITCOUNT destmap</span></span></code></pre></div><blockquote><p>即使一天产生一个亿的数据，Bitmap 占用的内存也不大，大约占 12 MB 的内存（10^8/8/1024/1024），7 天的 Bitmap 的内存开销约为 84 MB。同时我们最好给 Bitmap 设置过期时间，让 Redis 删除过期的打卡数据，节省内存。</p></blockquote><p>思路才是最重要，当我们遇到的统计场景只需要统计数据的二值状态，比如用户是否存在、 ip 是否是黑名单、以及签到打卡统计等场景就可以考虑使用 Bitmap。</p><p>只需要一个 bit 位就能表示 0 和 1。在统计海量数据的时候将大大减少内存占用。</p><h3 id="bitmaps与set对比" tabindex="-1">Bitmaps与set对比 <a class="header-anchor" href="#bitmaps与set对比" aria-label="Permalink to &quot;Bitmaps与set对比&quot;">​</a></h3><p>假设网站有1亿用户， 每天独立访问的用户有5千万， 如果每天用集合类型和Bitmaps分别存储活跃用户可以得到表</p><p><strong>存储一天活跃用户对比</strong></p><table><thead><tr><th>数据类型</th><th>每个用户id占用空间</th><th>需要存储的用户量</th><th>全部内存量</th></tr></thead><tbody><tr><td>集合类型</td><td>64位</td><td>50000000</td><td>64位*50000000 = 400MB</td></tr><tr><td>Bitmaps</td><td>1位</td><td>100000000</td><td>1位*100000000 = 12.5MB</td></tr></tbody></table><p><strong>存储独立用户空间对比</strong></p><p>很明显， 这种情况下使用Bitmaps能节省很多的内存空间， 尤其是随着时间推移节省的内存还是非常可观的</p><table><thead><tr><th>数据类型</th><th>一天</th><th>一个月</th><th>一年</th></tr></thead><tbody><tr><td>集合类型</td><td>400MB</td><td>12GB</td><td>144GB</td></tr><tr><td>Bitmaps</td><td>12.5MB</td><td>375MB</td><td>4.5GB</td></tr></tbody></table><p>但Bitmaps并不是万金油， 假如该网站每天的独立访问用户很少， 例如只有10万（大量的僵尸用户） ， 那么两者的对比如下表所示， 很显然， 这时候使用Bitmaps就不太合适了， 因为基本上大部分位都是0</p><p><strong>存储一天活跃用户对比（独立用户比较少）</strong></p><table><thead><tr><th>数据类型</th><th>每个userid占用空间</th><th>需要存储的用户量</th><th>全部内存量</th></tr></thead><tbody><tr><td>集合类型</td><td>64位</td><td>100000</td><td>64位*100000 = 800KB</td></tr><tr><td>Bitmaps</td><td>1位</td><td>100000000</td><td>1位*100000000 = 12.5MB</td></tr></tbody></table><p>主要功能：进行位操作。可以提高效率</p><h2 id="hyperloglog" tabindex="-1">HyperLogLog <a class="header-anchor" href="#hyperloglog" aria-label="Permalink to &quot;HyperLogLog&quot;">​</a></h2><h3 id="基本介绍" tabindex="-1">基本介绍 <a class="header-anchor" href="#基本介绍" aria-label="Permalink to &quot;基本介绍&quot;">​</a></h3><h4 id="uv-pv" tabindex="-1">UV &amp; PV <a class="header-anchor" href="#uv-pv" aria-label="Permalink to &quot;UV &amp; PV&quot;">​</a></h4><p>在工作当中，我们经常会遇到与统计相关的功能需求，比如统计网站PV（PageView页面访问量）,可以使用Redis的incr、incrby轻松实现。</p><blockquote><p>UV：全称UniqueVisitor，也叫独立访客量，是指通过互联网访问、浏览这个网页的自然人。1天内同一个用户多次访问该网站，只记录1次。</p><p>PV：<strong>全称</strong>PageView，也叫页面访问量或点击量，用户每访问网站的一个页面，记录1次PV，用户多次打开页面，则记录多次PV。往往用来衡量网站的流量。</p></blockquote><h4 id="基数问题" tabindex="-1">基数问题 <a class="header-anchor" href="#基数问题" aria-label="Permalink to &quot;基数问题&quot;">​</a></h4><p>但像UV（UniqueVisitor，独立访客）、独立IP数、搜索记录数等需要去重和计数的问题如何解决？这种求集合中不重复元素个数的问题称为基数问题。</p><blockquote><p>什么是基数？比如数据集 {1, 3, 5, 7, 5, 7, 8}， 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内，快速计算基数。</p></blockquote><p>解决基数问题有很多种方案：</p><blockquote><p>（1）数据存储在MySQL表中，使用distinct count计算不重复个数</p><p>（2）使用Redis提供的hash、set、bitmaps等数据结构来处理</p></blockquote><p>以上的方案结果精确，但随着数据不断增加，导致占用空间越来越大，对于非常大的数据集是不切实际的。</p><p>能否能够降低一定的精度来平衡存储空间？Redis推出了HyperLogLog</p><blockquote><p>Redis HyperLogLog 是用来做基数统计的算法，HyperLogLog 的优点是，在输入元素的数量或者体积非常非常大时，计算基数所需的空间总是固定的、并且是很小的。</p></blockquote><blockquote><p>在 Redis 里面，每个 HyperLogLog 键只需要花费 12 KB 内存，就可以计算接近 2^64 个不同元素的基数。这和计算基数时，元素越多耗费内存就越多的集合形成鲜明对比。</p></blockquote><blockquote><p>但是，因为 HyperLogLog 只会根据输入元素来计算基数，而不会储存输入元素本身，所以 HyperLogLog 不能像集合那样，返回输入的各个元素。</p></blockquote><h3 id="基本命令" tabindex="-1">基本命令 <a class="header-anchor" href="#基本命令" aria-label="Permalink to &quot;基本命令&quot;">​</a></h3><h4 id="pfadd" tabindex="-1">pfadd <a class="header-anchor" href="#pfadd" aria-label="Permalink to &quot;pfadd&quot;">​</a></h4><blockquote><p>将所有元素添加到指定HyperLogLog中。如果执行命令后HLL估计的近似基数发生变化，则返回1，否则返回0</p></blockquote><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 添加指定元素到 HyperLogLog 中</span></span>
<span class="line"><span style="color:#A6ACCD;">pfadd key element </span><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">element </span><span style="color:#89DDFF;">...]</span></span>
<span class="line"><span style="color:#A6ACCD;">pfadd lan java c c</span><span style="color:#89DDFF;">#</span></span>
<span class="line"><span style="color:#A6ACCD;">pfadd lan redis mysql</span></span>
<span class="line"><span style="color:#A6ACCD;">pfadd lan redis</span></span>
<span class="line"><span style="color:#A6ACCD;">pfadd lan c</span><span style="color:#89DDFF;">++</span></span>
<span class="line"><span style="color:#A6ACCD;">pfadd lan1 c</span><span style="color:#89DDFF;">++</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301251008927.png" alt="image-20230125100836793" style="zoom:67%;"><h4 id="pfcount" tabindex="-1">pfcount <a class="header-anchor" href="#pfcount" aria-label="Permalink to &quot;pfcount&quot;">​</a></h4><blockquote><p>计算HLL的近似基数，计算多个HLL，比如用HLL存储每天的UV，计算一周的UV可以使用7天的UV合并计算即可</p></blockquote><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 返回给定 HyperLogLog 的基数估算值。</span></span>
<span class="line"><span style="color:#A6ACCD;">pfcount key </span><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">key </span><span style="color:#89DDFF;">...]</span></span>
<span class="line"><span style="color:#A6ACCD;">pfcount lan </span><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 结果是</span><span style="color:#F78C6C;">6</span><span style="color:#A6ACCD;">，即添加进入的元素总数</span></span>
<span class="line"><span style="color:#A6ACCD;">pfcount lan lan1  </span><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 自动去重</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301251011972.png" alt="image-20230125101135850" style="zoom:67%;"><h4 id="pfmerge" tabindex="-1">pfmerge <a class="header-anchor" href="#pfmerge" aria-label="Permalink to &quot;pfmerge&quot;">​</a></h4><blockquote><p>将一个或多个HLL合并后的结果存储在另一个HLL中，比如每月活跃用户可以使用每天的活跃用户来合并计算可得</p></blockquote><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 将多个 HyperLogLog 合并为一个 HyperLogLog</span></span>
<span class="line"><span style="color:#A6ACCD;">pfmerge destkey sourcekey </span><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">sourcekey </span><span style="color:#89DDFF;">...]</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> lan3不存在，lan1和lan2进行合并，自动去重，结果保存到lan3中</span></span>
<span class="line"><span style="color:#A6ACCD;">pfmerge lan3 lan lan1</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301251013150.png" alt="image-20230125101354017" style="zoom:67%;"><h3 id="实战演练-1" tabindex="-1">实战演练 <a class="header-anchor" href="#实战演练-1" aria-label="Permalink to &quot;实战演练&quot;">​</a></h3><p>我们直接利用单元测试，向HyperLogLog中添加100万条数据，看看内存占用和统计效果如何</p><div class="language-java"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">@</span><span style="color:#C792EA;">Resource</span></span>
<span class="line"><span style="color:#C792EA;">private</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">StringRedisTemplate</span><span style="color:#A6ACCD;"> stringRedisTemplate</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">@</span><span style="color:#C792EA;">Test</span></span>
<span class="line"><span style="color:#C792EA;">void</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">testHyperLogLog</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#C792EA;">String</span><span style="color:#89DDFF;">[]</span><span style="color:#A6ACCD;"> values </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">new</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">String</span><span style="color:#89DDFF;">[</span><span style="color:#F78C6C;">1000</span><span style="color:#89DDFF;">];</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#C792EA;">int</span><span style="color:#A6ACCD;"> j </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;font-style:italic;">for</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">(</span><span style="color:#C792EA;">int</span><span style="color:#A6ACCD;"> i </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">;</span><span style="color:#A6ACCD;"> i </span><span style="color:#89DDFF;">&lt;</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1000000</span><span style="color:#89DDFF;">;</span><span style="color:#A6ACCD;"> i</span><span style="color:#89DDFF;">++)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">//每隔1000条发一次</span></span>
<span class="line"><span style="color:#A6ACCD;">        j </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> i </span><span style="color:#89DDFF;">%</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1000</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">        values</span><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">j</span><span style="color:#89DDFF;">]</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">user_</span><span style="color:#89DDFF;">&quot;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">+</span><span style="color:#A6ACCD;"> i</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;font-style:italic;">if</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">j </span><span style="color:#89DDFF;">==</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">999</span><span style="color:#89DDFF;">){</span></span>
<span class="line"><span style="color:#89DDFF;">            </span><span style="color:#676E95;font-style:italic;">// 发送到Redis</span></span>
<span class="line"><span style="color:#A6ACCD;">            stringRedisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">opsForHyperLogLog</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">add</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">hl2</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> values</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 统计数量</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#C792EA;">Long</span><span style="color:#A6ACCD;"> count </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> stringRedisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">opsForHyperLogLog</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">size</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">hl2</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#A6ACCD;">    System</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">out</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">println</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">count = </span><span style="color:#89DDFF;">&quot;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">+</span><span style="color:#A6ACCD;"> count</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301241328882.png" alt="image-20230124132820780" style="zoom:67%;"><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301241328870.png" alt="image-20230124132844774" style="zoom:67%;"><h3 id="应用场景⭐" tabindex="-1">应用场景⭐ <a class="header-anchor" href="#应用场景⭐" aria-label="Permalink to &quot;应用场景⭐&quot;">​</a></h3><p>在移动互联网的业务场景中，<strong>数据量很大</strong>，我们需要保存这样的信息：一个 key 关联了一个数据集合，同时对这个数据集合做统计。</p><ul><li>统计一个 <code>APP</code> 的日活、月活数；</li><li>统计一个页面的每天被多少个不同账户访问量（Unique Visitor，UV））；</li><li>统计用户每天搜索不同词条的个数；</li><li>统计注册 IP 数。</li></ul><p>通常情况下，我们面临的用户数量以及访问量都是巨大的，比如<strong>百万、千万级别的用户数量，或者千万级别、甚至亿级别</strong>的访问信息。</p><h4 id="百万级网页-uv-计数" tabindex="-1">百万级网页 UV 计数 <a class="header-anchor" href="#百万级网页-uv-计数" aria-label="Permalink to &quot;百万级网页 UV 计数&quot;">​</a></h4><p>Redis HyperLogLog 优势在于只需要花费 12 KB 内存，就可以计算接近 2^64 个元素的基数，和元素越多就越耗费内存的 Set 和 Hash 类型相比，HyperLogLog 就非常节省空间。所以，非常适合统计百万级以上的网页 UV 的场景。</p><p>在统计 UV 时，你可以用 PFADD 命令（用于向 HyperLogLog 中添加新元素）把访问页面的每个用户都添加到 HyperLogLog 中。</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">pfadd page1:uv user1 user2 user3 user4 user5</span></span></code></pre></div><p>接下来，就可以用 PFCOUNT 命令直接获得 page1 的 UV 值了，这个命令的作用就是返回 HyperLogLog 的统计结果。</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">pfcount page1:uv</span></span></code></pre></div><blockquote><p>不过，有一点需要你注意一下，HyperLogLog 的统计规则是基于概率完成的，所以它给出的统计结果是有一定误差的，标准误算率是 0.81%。这也就意味着，你使用 HyperLogLog 统计的 UV 是 100 万，但实际的 UV 可能是 101 万。虽然误差率不算大，但是，如果你需要精确统计结果的话，最好还是继续用 Set 或 Hash 类型。</p></blockquote><h2 id="geospatial" tabindex="-1">Geospatial <a class="header-anchor" href="#geospatial" aria-label="Permalink to &quot;Geospatial&quot;">​</a></h2><blockquote><p>Redis 3.2 中增加了对GEO类型的支持。GEO，Geographic，地理信息的缩写。该类型，就是元素的2维坐标，在地图上就是经纬度。redis基于该类型，提供了经纬度设置，查询，范围查询，距离查询，经纬度Hash等常见操作。</p></blockquote><h3 id="内部实现-1" tabindex="-1">内部实现 <a class="header-anchor" href="#内部实现-1" aria-label="Permalink to &quot;内部实现&quot;">​</a></h3><p>GEO 本身并没有设计新的底层数据结构，而是<strong>直接使用了 Sorted Set 集合类型</strong>。</p><blockquote><p>GEO 类型使用 GeoHash 编码方法实现了经纬度到 Sorted Set 中元素权重分数的转换，这其中的两个关键机制就是「对二维地图做区间划分」和「对区间进行编码」。一组经纬度落在某个区间后，就用区间的编码值来表示，并把编码值作为 Sorted Set 元素的权重分数。</p></blockquote><blockquote><p>这样一来，我们就可以把经纬度保存到 Sorted Set 中，利用 Sorted Set 提供的“按权重进行有序范围查找”的特性，实现 LBS 服务中频繁使用的“搜索附近”的需求。</p></blockquote><h3 id="基本命令-1" tabindex="-1">基本命令 <a class="header-anchor" href="#基本命令-1" aria-label="Permalink to &quot;基本命令&quot;">​</a></h3><h4 id="geoadd" tabindex="-1">geoadd <a class="header-anchor" href="#geoadd" aria-label="Permalink to &quot;geoadd&quot;">​</a></h4><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 添加地理位置（经度，纬度，名称）</span></span>
<span class="line"><span style="color:#A6ACCD;">geoadd key longitude latitude member </span><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">longitude latitude member </span><span style="color:#89DDFF;">...]</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 分别添加上海重庆深圳和北京的经纬度</span></span>
<span class="line"><span style="color:#A6ACCD;">geoadd china:city </span><span style="color:#F78C6C;">121.47</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">31.23</span><span style="color:#A6ACCD;"> shanghai</span></span>
<span class="line"><span style="color:#A6ACCD;">geoadd china:city </span><span style="color:#F78C6C;">106.50</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">29.53</span><span style="color:#A6ACCD;"> chongqing </span><span style="color:#F78C6C;">114.05</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">22.52</span><span style="color:#A6ACCD;"> shenzhen </span><span style="color:#F78C6C;">116.38</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">39.90</span><span style="color:#A6ACCD;"> beijing</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301250955570.png" alt="image-20230125095520398" style="zoom:67%;"><blockquote><p>两极无法直接添加，一般会下载城市数据，直接通过 Java 程序一次性导入。</p><p>有效的经度从 -180 度到 180 度。有效的纬度从 -85.05112878 度到 85.05112878 度。</p><p>当坐标位置超出指定范围时，该命令将会返回一个错误。已经添加的数据，是无法再次往里面添加的。</p></blockquote><h4 id="geopos" tabindex="-1">geopos <a class="header-anchor" href="#geopos" aria-label="Permalink to &quot;geopos&quot;">​</a></h4><blockquote><p>获得指定地区的坐标值，不存在的返回 nil</p></blockquote><div class="language-assembly"><button title="Copy Code" class="copy"></button><span class="lang">assembly</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">geopos key member [member ...]</span></span>
<span class="line"><span style="color:#A6ACCD;">geopos china:city shanghai</span></span>
<span class="line"><span style="color:#A6ACCD;">geopos china:city chongqing</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301250957080.png" alt="image-20230125095704951" style="zoom:67%;"><h4 id="geodist" tabindex="-1">geodist <a class="header-anchor" href="#geodist" aria-label="Permalink to &quot;geodist&quot;">​</a></h4><blockquote><p>获取两个位置之间的直线距离</p></blockquote><div class="language-assembly"><button title="Copy Code" class="copy"></button><span class="lang">assembly</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">geodist key member1 member2 [m|km|ft英里|mi英尺]</span></span>
<span class="line"><span style="color:#A6ACCD;">geodist china:city chongqing shanghai km</span></span>
<span class="line"><span style="color:#A6ACCD;">geodist china:city chongqing shanghai m</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301250958217.png" alt="image-20230125095858095" style="zoom:67%;"><blockquote><p>m 表示单位为米[默认值]。</p><p>km 表示单位为千米。</p><p>mi 表示单位为英里。</p><p>ft 表示单位为英尺。</p><p>如果用户没有显式地指定单位参数， 那么 GEODIST 默认使用米作为单位</p></blockquote><h4 id="georadius" tabindex="-1">georadius <a class="header-anchor" href="#georadius" aria-label="Permalink to &quot;georadius&quot;">​</a></h4><div class="language-assembly"><button title="Copy Code" class="copy"></button><span class="lang">assembly</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;"># 以给定的经纬度为中心，找出某一半径内的元素：经度 纬度 距离 单位</span></span>
<span class="line"><span style="color:#A6ACCD;">georadius &lt;key&gt; &lt;longitude&gt;&lt;latitude&gt;radius m|km|ft|mi  </span></span>
<span class="line"><span style="color:#A6ACCD;">georadius china:city 110 30 1000 km</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301251000619.png" alt="image-20230125100012481" style="zoom:67%;"><h3 id="应用场景-5" tabindex="-1">应用场景 <a class="header-anchor" href="#应用场景-5" aria-label="Permalink to &quot;应用场景&quot;">​</a></h3><h4 id="滴滴叫车" tabindex="-1">滴滴叫车 <a class="header-anchor" href="#滴滴叫车" aria-label="Permalink to &quot;滴滴叫车&quot;">​</a></h4><blockquote><p>这里以滴滴叫车的场景为例，介绍下具体如何使用 GEO 命令：GEOADD 和 GEORADIUS 这两个命令。</p></blockquote><blockquote><p>假设车辆 ID 是 33，经纬度位置是（116.034579，39.030452），我们可以用一个 GEO 集合保存所有车辆的经纬度，集合 key 是 cars:locations。执行这个命令，可以把 ID 号为 33 的车辆的当前经纬度位置存入 GEO 集合中</p></blockquote><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">geoadd cars:locations </span><span style="color:#F78C6C;">116.034579</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">39.030452</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">33</span></span></code></pre></div><p>当用户想要寻找自己附近的网约车时，LBS 应用就可以使用 GEORADIUS 命令。</p><blockquote><p>例如，LBS 应用执行下面的命令时，Redis 会根据输入的用户的经纬度信息（116.054579，39.030452 ），查找以这个经纬度为中心的 5 公里内的车辆信息，并返回给 LBS 应用。</p></blockquote><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;"># 结果为33</span></span>
<span class="line"><span style="color:#A6ACCD;">georadius cars:locations </span><span style="color:#F78C6C;">116.054579</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">39.030452</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">5</span><span style="color:#A6ACCD;"> km ASC COUNT </span><span style="color:#F78C6C;">10</span></span></code></pre></div><h1 id="redis-conf-配置文件-⭐" tabindex="-1">redis.conf 配置文件 ⭐ <a class="header-anchor" href="#redis-conf-配置文件-⭐" aria-label="Permalink to &quot;redis.conf 配置文件 ⭐&quot;">​</a></h1><h2 id="配置详解" tabindex="-1">配置详解 <a class="header-anchor" href="#配置详解" aria-label="Permalink to &quot;配置详解&quot;">​</a></h2><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.1.30/202212221119486.png" alt="image-20221222111930293" style="zoom:67%;"><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.1.30/202212221120897.png" alt="image-20221222112019722" style="zoom:80%;"><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.1.30/202212221120987.png" alt="image-20221222112046780" style="zoom:80%;"><h2 id="单位" tabindex="-1">单位 <a class="header-anchor" href="#单位" aria-label="Permalink to &quot;单位&quot;">​</a></h2><blockquote><p>配置大小单位,开头定义了一些基本的度量单位，<strong>只支持bytes，不支持bit</strong>，大小写不敏感</p></blockquote><div class="language-ini"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#F07178;">1k</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;">&gt; 1000 bytes</span></span>
<span class="line"><span style="color:#F07178;">1kb</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;">&gt; 1024 bytes</span></span>
<span class="line"><span style="color:#F07178;">1m</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;">&gt; 1000000 bytes</span></span>
<span class="line"><span style="color:#F07178;">1mb</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;">&gt; 1024*1024 bytes</span></span>
<span class="line"><span style="color:#F07178;">1g</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;">&gt; 1000000000 bytes</span></span>
<span class="line"><span style="color:#F07178;">1gb</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;">&gt; 1024*1024*1024 bytes</span></span></code></pre></div><h2 id="include" tabindex="-1">include <a class="header-anchor" href="#include" aria-label="Permalink to &quot;include&quot;">​</a></h2><blockquote><p>包含，类似jsp中的include，多实例的情况可以把公用的配置文件提取出来，比如自己想建立一个以端口号为6380启动的配置文件，提取原来的配置文件，然后再进行新增覆盖对应内容即可</p></blockquote><div class="language-ini"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">include /myredis/redis.conf</span></span>
<span class="line"><span style="color:#A6ACCD;">pidfile /var/run/redis_6380.pid</span></span>
<span class="line"><span style="color:#A6ACCD;">port 6380</span></span>
<span class="line"><span style="color:#A6ACCD;">dbfilename dump6379.rdb</span></span></code></pre></div><h2 id="网络配置-⭐" tabindex="-1">网络配置 ⭐ <a class="header-anchor" href="#网络配置-⭐" aria-label="Permalink to &quot;网络配置 ⭐&quot;">​</a></h2><h3 id="bind" tabindex="-1">bind <a class="header-anchor" href="#bind" aria-label="Permalink to &quot;bind&quot;">​</a></h3><blockquote><p>默认情况bind=127.0.0.1只能接受本机的访问请求。不写的情况下，无限制接受任何ip地址的访问</p><p>生产环境肯定要写你应用服务器的地址；服务器是需要远程访问的，所以需要将其注释掉</p><p><strong>如果开启了protected-mode，那么在没有设定bind ip且没有设密码的情况下，Redis只允许接受本机的响应</strong></p></blockquote><div class="language-ini"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">bind 0.0.0.0</span></span></code></pre></div><h3 id="protected-mode" tabindex="-1">protected-mode <a class="header-anchor" href="#protected-mode" aria-label="Permalink to &quot;protected-mode&quot;">​</a></h3><blockquote><p>no表示允许远程访问，将本机访问保护模式设置no,不然外界访问就会出错，和上面bind都要进行修改</p></blockquote><div class="language-ini"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">protected-mode no</span></span></code></pre></div><h3 id="port" tabindex="-1">port <a class="header-anchor" href="#port" aria-label="Permalink to &quot;port&quot;">​</a></h3><blockquote><p>端口号，默认 6379</p></blockquote><div class="language-ini"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">port 6379</span></span></code></pre></div><h3 id="tcp-backlog" tabindex="-1">tcp-backlog <a class="header-anchor" href="#tcp-backlog" aria-label="Permalink to &quot;tcp-backlog&quot;">​</a></h3><blockquote><p>设置tcp的backlog，backlog其实是一个连接队列，backlog队列总和=未完成三次握手队列 + 已经完成三次握手队列。<strong>在高并发环境下你需要一个高backlog值来避免慢客户端连接问题</strong>。</p></blockquote><blockquote><p>注意Linux内核会将这个值减小到/proc/sys/net/core/somaxconn的值（128），所以需要确认增大/proc/sys/net/core/somaxconn和/proc/sys/net/ipv4/tcp_max_syn_backlog（128）两个值来达到想要的效果</p></blockquote><div class="language-ini"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">tcp-backlog 511</span></span></code></pre></div><h3 id="timeout" tabindex="-1">timeout <a class="header-anchor" href="#timeout" aria-label="Permalink to &quot;timeout&quot;">​</a></h3><blockquote><p>一个空闲的客户端维持多少秒会关闭，0表示关闭该功能。即<strong>永不关闭</strong></p></blockquote><div class="language-ini"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">timeout 0</span></span></code></pre></div><h3 id="tcp-keepalive" tabindex="-1">tcp-keepalive <a class="header-anchor" href="#tcp-keepalive" aria-label="Permalink to &quot;tcp-keepalive&quot;">​</a></h3><blockquote><p>对访问客户端的一种心跳检测，每个n秒检测一次。</p><p>单位为秒，如果设置为0，则不会进行Keepalive检测，建议设置成60</p></blockquote><div class="language-ini"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">tcp-keepalive 0</span></span></code></pre></div><h2 id="general-通用" tabindex="-1">GENERAL 通用 <a class="header-anchor" href="#general-通用" aria-label="Permalink to &quot;GENERAL 通用&quot;">​</a></h2><h3 id="daemonize" tabindex="-1">daemonize <a class="header-anchor" href="#daemonize" aria-label="Permalink to &quot;daemonize&quot;">​</a></h3><blockquote><p>是否为后台进程，设置为yes，<strong>守护进程，后台启动</strong>。Docker启动redis时要将它修改为no</p></blockquote><div class="language-ini"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">daemonize yes</span></span></code></pre></div><p><code>Redis</code>采用的是单进程多线程的模式，<code>daemonize</code>是用来指定<code>redis</code>是否要用守护线程的方式启动。默认情况下，<code>Redis</code>不作为守护进程运行。如果需要，请使用“是”。</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">daemonize no </span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">//当前界面将进入redis的命令行界面，exit强制退出或者关闭连接工具(putty，</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">//xshell等)都会导致redis进程退出。</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">daemonize yes     </span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">//代表开启守护进程模式。在该模式下，redis 会在后台运行，并将进程 pid 号写入</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">//至 redis.conf 选项 pidfile 设置的文件中，此时 redis 将一直运行，除非手动kill该进程。</span></span></code></pre></div><h3 id="pidfile" tabindex="-1">pidfile <a class="header-anchor" href="#pidfile" aria-label="Permalink to &quot;pidfile&quot;">​</a></h3><blockquote><p>存放pid文件的位置，每个实例会产生一个不同的pid文件</p></blockquote><div class="language-ini"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">pidfile </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">/var/run/redis_6379.pid</span><span style="color:#89DDFF;">&quot;</span></span></code></pre></div><h3 id="loglevel-logfile" tabindex="-1">loglevel &amp; logfile <a class="header-anchor" href="#loglevel-logfile" aria-label="Permalink to &quot;loglevel &amp; logfile&quot;">​</a></h3><blockquote><p>指定日志记录级别，Redis总共支持四个级别：debug、verbose、notice、warning，默认为notice，四个级别根据使用阶段来选择，生产环境选择notice 或者warning</p></blockquote><div class="language-ini"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;"># 日志记录级别</span></span>
<span class="line"><span style="color:#A6ACCD;">loglevel notice</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 日志文件名称</span></span>
<span class="line"><span style="color:#A6ACCD;">logfile </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">server_log.txt</span><span style="color:#89DDFF;">&quot;</span></span></code></pre></div><h3 id="databases-16" tabindex="-1">databases 16 <a class="header-anchor" href="#databases-16" aria-label="Permalink to &quot;databases 16&quot;">​</a></h3><blockquote><p>设定库的数量默认16，默认数据库为0，可以使用SELECT id命令在连接上指定数据库id</p></blockquote><div class="language-ini"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">databases 16</span></span></code></pre></div><h2 id="安全security⭐" tabindex="-1">安全Security⭐ <a class="header-anchor" href="#安全security⭐" aria-label="Permalink to &quot;安全Security⭐&quot;">​</a></h2><h3 id="永久设置密码" tabindex="-1">永久设置密码 <a class="header-anchor" href="#永久设置密码" aria-label="Permalink to &quot;永久设置密码&quot;">​</a></h3><p>设置密码，去掉前面的注释符号，redis6.0配置文件设置密码不生效了，很奇怪</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">vim</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">/etc/redis.conf</span></span></code></pre></div><div class="language-ini"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;"># requirepass foobared</span></span>
<span class="line"><span style="color:#A6ACCD;">requirepass 315217</span></span></code></pre></div><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">systemctl</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">restart</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">redis</span></span></code></pre></div><h3 id="临时设置密码" tabindex="-1">临时设置密码 <a class="header-anchor" href="#临时设置密码" aria-label="Permalink to &quot;临时设置密码&quot;">​</a></h3><p>重启redis服务器，密码就还原了</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;"># 设置密码</span></span>
<span class="line"><span style="color:#FFCB6B;">config</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">get</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">requirepass</span></span>
<span class="line"><span style="color:#FFCB6B;">config</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">set</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">requirepass</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1234</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 清除密码</span></span>
<span class="line"><span style="color:#FFCB6B;">config</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">set</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">requirepass</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;&quot;</span></span></code></pre></div><h3 id="连接使用" tabindex="-1">连接使用 <a class="header-anchor" href="#连接使用" aria-label="Permalink to &quot;连接使用&quot;">​</a></h3><p>设置密码后，要添加密码才能使用</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;"># 用auth方式加密</span></span>
<span class="line"><span style="color:#FFCB6B;">redis-cli</span></span>
<span class="line"><span style="color:#FFCB6B;">auth</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">315217</span></span>
<span class="line"><span style="color:#82AAFF;">set</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">k1</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">v1</span></span></code></pre></div><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;"># -a方式输入密码</span></span>
<span class="line"><span style="color:#FFCB6B;">redis-cli</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-a</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">315217</span></span>
<span class="line"><span style="color:#82AAFF;">set</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">k2</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">v2</span></span></code></pre></div><h2 id="limits-限制⭐" tabindex="-1">Limits 限制⭐ <a class="header-anchor" href="#limits-限制⭐" aria-label="Permalink to &quot;Limits 限制⭐&quot;">​</a></h2><h3 id="maxclients" tabindex="-1">maxclients <a class="header-anchor" href="#maxclients" aria-label="Permalink to &quot;maxclients&quot;">​</a></h3><blockquote><p>设置redis同时可以与多少个客户端进行连接。默认情况下为10000个客户端。如果达到了此限制，redis则会拒绝新的连接请求，并且向这些连接请求方发出“max number of clients reached”以作回应。</p></blockquote><div class="language-ini"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">maxclients 10000</span></span></code></pre></div><h3 id="maxmemory" tabindex="-1">maxmemory <a class="header-anchor" href="#maxmemory" aria-label="Permalink to &quot;maxmemory&quot;">​</a></h3><blockquote><p>建议必须设置，否则，将内存占满，造成服务器宕机。设置redis可以使用的内存量。一旦到达内存使用上限，redis将会试图移除内部数据，移除规则可以通过maxmemory-policy来指定。</p></blockquote><blockquote><p>如果redis无法根据移除规则来移除内存中的数据，或者设置了“不允许移除”，那么redis则会针对那些需要申请内存的指令返回错误信息，比如SET、LPUSH等。</p></blockquote><blockquote><p>但是对于无内存申请的指令，仍然会正常响应，比如GET等。如果你的redis是主redis（说明你的redis有从redis），那么在设置内存使用上限时，需要在系统中留出一些内存空间给同步队列缓存，只有在你设置的是“不移除”的情况下，才不用考虑这个因素。</p></blockquote><div class="language-ini"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">maxmemory 400mb</span></span></code></pre></div><h3 id="maxmemory-policy" tabindex="-1">maxmemory-policy <a class="header-anchor" href="#maxmemory-policy" aria-label="Permalink to &quot;maxmemory-policy&quot;">​</a></h3><blockquote><ul><li>volatile-lru：使用LRU算法移除key，只对设置了过期时间的键；（最近最少使用）</li><li>allkeys-lru：在所有集合key中，使用LRU算法移除key</li><li>volatile-random：在过期集合中移除随机的key，只对设置了过期时间的键</li><li>allkeys-random：在所有集合key中，移除随机的key</li><li>volatile-ttl：移除那些TTL值最小的key，即那些最近要过期的key</li><li>noeviction：不进行移除。针对写操作，只是返回错误信息</li></ul></blockquote><div class="language-ini"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">maxmemory-policy noeviction</span></span></code></pre></div><h3 id="maxmemory-samples" tabindex="-1">maxmemory-samples <a class="header-anchor" href="#maxmemory-samples" aria-label="Permalink to &quot;maxmemory-samples&quot;">​</a></h3><blockquote><ul><li>设置样本数量，LRU算法和最小TTL算法都并非是精确的算法，而是估算值，所以你可以设置样本的大小，redis默认会检查这么多个key并选择其中LRU的那个。</li><li>一般设置3到7的数字，数值越小样本越不准确，但性能消耗越小</li></ul></blockquote><div class="language-ini"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">maxmemory-samples 5</span></span></code></pre></div><h3 id="replica-ignore-maxmemory" tabindex="-1">replica-ignore-maxmemory <a class="header-anchor" href="#replica-ignore-maxmemory" aria-label="Permalink to &quot;replica-ignore-maxmemory&quot;">​</a></h3><p>副本忽略最大内存</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">replica</span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">ignore</span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">maxmemory yes</span></span></code></pre></div><p>从<code>Redis 5</code>开始，默认情况下，<code>replica</code>节点会忽略<code>maxmemory</code>设置（除非在发生<code>failover</code>后，此节点被提升为<code>master</code>节点）。</p><p>这意味着只有<code>master</code>才会执行过期删除策略，并且<code>master</code>在删除键之后会对<code>replica</code>发送<code>DEL</code>命令。</p><p>这个行为保证了<code>master</code>和<code>replicas</code>的一致性，并且这通常也是你需要的，但是若你的<code>replica</code>节点是可写的，或者你希望<code>replica</code>节点有不同的内存配置，并且你确保所有到<code>replica</code>写操作都幂等的，那么你可以修改这个默认的行为 （请确保你明白你在做什么）。</p><p>**「注意」**默认情况下<code>replica</code>节点不会执行过期策略，它有可能使用了超过<code>maxmemory</code>设定的值的内存。因此你需要监控<code>replicas</code>节点所在的机器并且确保在<code>master</code>节点到达配置的<code>maxmemory</code>大小时，<code>replicas</code>节点不会超过物理内存的大小。</p><h1 id="redisearch-redisjson" tabindex="-1">RediSearch + RedisJSON <a class="header-anchor" href="#redisearch-redisjson" aria-label="Permalink to &quot;RediSearch + RedisJSON&quot;">​</a></h1><p><a href="https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&amp;mid=2247498172&amp;idx=1&amp;sn=f1df934fe4384a27e5ec6340a4642789&amp;chksm=fc2c45b4cb5bcca22e8ac4384c27c93e805e0d1a85432a6c2d69c23e6b96e8677853d92a48cb&amp;mpshare=1&amp;scene=23&amp;srcid=0511TYAaDopOjhhZLHE5LQYX&amp;sharer_sharetime=1652199711433&amp;sharer_shareid=29b8a04db1dbd975e3bf4e9f47e7ac67#rd" target="_blank" rel="noreferrer">比 Elasticsearch 更快！RediSearch + RedisJSON = 王炸！ (qq.com)</a></p><blockquote><p>Redis是开发中非常常用的内存数据存储中间件，之前基本上用它来做内存存储使用。最近发现Redis推出了很多增强模块，例如通过RedisJSON可以支持原生JSON对象的存储，使用RediSearch可以作为搜索引擎使用，并且支持中文搜索！今天给大家带来RediSearch+RedisJSON作为搜索引擎的使用实践，希望对大家有所帮助！</p></blockquote><ul><li>官方文档：<a href="https://developer.redis.com/howtos/redisjson/" target="_blank" rel="noreferrer">https://developer.redis.com/howtos/redisjson/</a></li><li>参考手册：<a href="https://oss.redis.com/redisearch/" target="_blank" rel="noreferrer">https://oss.redis.com/redisearch/</a></li><li>性能测试：<a href="https://redis.com/blog/search-benchmarking-redisearch-vs-elasticsearch/" target="_blank" rel="noreferrer">https://redis.com/blog/search-benchmarking-redisearch-vs-elasticsearch/</a></li></ul><h2 id="redismod简介" tabindex="-1">RedisMod简介 <a class="header-anchor" href="#redismod简介" aria-label="Permalink to &quot;RedisMod简介&quot;">​</a></h2><p>首先介绍下RedisMod这个东西，它是一系列Redis的增强模块。有了RedisMod的支持，Redis的功能将变得非常强大。目前RedisMod中包含了如下增强模块：</p><ul><li>RediSearch：一个功能齐全的搜索引擎；</li><li>RedisJSON：对JSON类型的原生支持；</li><li>RedisTimeSeries：时序数据库支持；</li><li>RedisGraph：图数据库支持；</li><li>RedisBloom：概率性数据的原生支持；</li><li>RedisGears：可编程的数据处理；</li><li>RedisAI：机器学习的实时模型管理和部署。</li></ul><h2 id="安装-使用" tabindex="-1">安装 &amp; 使用 <a class="header-anchor" href="#安装-使用" aria-label="Permalink to &quot;安装 &amp; 使用&quot;">​</a></h2><blockquote><p>首先我们需要安装带所有RedisMod的Redis，使用Docker来安装非常方便的！</p></blockquote><p>使用如下命令下载RedisMod的镜像；</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">docker</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">pull</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">redislabs/redismod:preview</span></span></code></pre></div><p>在容器中运行RedisMod服务。</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">docker</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">run</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-p</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">6379</span><span style="color:#C3E88D;">:6379</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">--name</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">redismod</span><span style="color:#A6ACCD;"> \</span></span>
<span class="line"><span style="color:#A6ACCD;">-v </span><span style="color:#C3E88D;">/mydata/redismod/data:/data</span><span style="color:#A6ACCD;"> \</span></span>
<span class="line"><span style="color:#A6ACCD;">-d </span><span style="color:#C3E88D;">redislabs/redismod:preview</span></span></code></pre></div><p>正常使用即可</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">docker</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">exec</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-it</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">redismod</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">bash</span></span>
<span class="line"><span style="color:#FFCB6B;">redis-cli</span></span></code></pre></div><h2 id="redisjson" tabindex="-1">RedisJSON <a class="header-anchor" href="#redisjson" aria-label="Permalink to &quot;RedisJSON&quot;">​</a></h2><blockquote><p>有了RedisJSON模块，Redis就可以存储原生JSON类型数据了，通过它你可以很方便地访问JSON中的各个属性，类似在MongoDB中那样，下面我们就来体验下，这里我们将使用RedisInsight 来操作Redis。</p></blockquote><ul><li>首先通过<code>JSON.SET</code>命令向Redis中添加JSON类型键值对，几个商品对象数据，由于JSON是树形结构的，使用<code>$</code>符号代表往JSON的根节点中添加数据；</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">JSON</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">SET product:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> $ </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">{&quot;id&quot;:1,&quot;productSn&quot;:&quot;7437788&quot;,&quot;name&quot;:&quot;小米8&quot;,&quot;subTitle&quot;:&quot;全面屏游戏智能手机 6GB+64GB 黑色 全网通4G 双卡双待&quot;,&quot;brandName&quot;:&quot;小米&quot;,&quot;price&quot;:2699,&quot;count&quot;:1}</span><span style="color:#89DDFF;">&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">JSON</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">SET product:</span><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;"> $ </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">{&quot;id&quot;:2,&quot;productSn&quot;:&quot;7437789&quot;,&quot;name&quot;:&quot;红米5A&quot;,&quot;subTitle&quot;:&quot;全网通版 3GB+32GB 香槟金 移动联通电信4G手机 双卡双待&quot;,&quot;brandName&quot;:&quot;小米&quot;,&quot;price&quot;:649,&quot;count&quot;:5}</span><span style="color:#89DDFF;">&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">JSON</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">SET product:</span><span style="color:#F78C6C;">3</span><span style="color:#A6ACCD;"> $ </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">{&quot;id&quot;:3,&quot;productSn&quot;:&quot;7437799&quot;,&quot;name&quot;:&quot;Apple iPhone 8 Plus&quot;,&quot;subTitle&quot;:&quot;64GB 红色特别版 移动联通电信4G手机&quot;,&quot;brandName&quot;:&quot;苹果&quot;,&quot;price&quot;:5499,&quot;count&quot;:10}</span><span style="color:#89DDFF;">&#39;</span></span></code></pre></div><ul><li>数据插入成功后，在RedisInsight中将看到如下信息，JSON数据支持格式化高亮显示；</li></ul><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.5.13/202205131719708.png" alt="image-20220513171921617" style="zoom:67%;"><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.5.13/202205131720326.png" alt="image-20220513172003250" style="zoom:80%;"><ul><li>接下来可以通过<code>JSON.GET</code>命令获取JSON类型键值对的值；</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">JSON</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">GET product:</span><span style="color:#F78C6C;">1</span></span></code></pre></div><ul><li>也可以只获取值的指定属性，在RedisJSON中，获取JSON对象中的属性时需要以<code>.</code>开头；</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">JSON</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">GET product:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">name </span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">subTitle</span></span></code></pre></div><ul><li>也可以只获取值的指定属性，在RedisJSON中，获取JSON对象中的属性时需要以<code>.</code>开头；</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">JSON</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">GET product:</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">name </span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">subTitle</span></span></code></pre></div><h2 id="redisearch" tabindex="-1">RediSearch <a class="header-anchor" href="#redisearch" aria-label="Permalink to &quot;RediSearch&quot;">​</a></h2><blockquote><p>通过RediSearch模块，Redis可以变成一个功能强大的全文搜索引擎，并且原生支持中文搜索，下面我们就来体验</p></blockquote><ul><li>使用RediSearch来搜索数据之前，我们得先创建下索引，建立索引的语法有点复杂，我们先来看下；</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">FT</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">CREATE </span><span style="color:#89DDFF;">{</span><span style="color:#A6ACCD;">index</span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">ON </span><span style="color:#89DDFF;">{</span><span style="color:#A6ACCD;">data_type</span><span style="color:#89DDFF;">}]</span></span>
<span class="line"><span style="color:#A6ACCD;">     </span><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">PREFIX </span><span style="color:#89DDFF;">{</span><span style="color:#A6ACCD;">count</span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span><span style="color:#A6ACCD;">prefix</span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> [</span><span style="color:#89DDFF;">{</span><span style="color:#A6ACCD;">prefix</span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">..]</span></span>
<span class="line"><span style="color:#A6ACCD;">     </span><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">LANGUAGE </span><span style="color:#89DDFF;">{</span><span style="color:#A6ACCD;">default_lang</span><span style="color:#89DDFF;">}]</span></span>
<span class="line"><span style="color:#A6ACCD;">  SCHEMA </span><span style="color:#89DDFF;">{</span><span style="color:#A6ACCD;">identifier</span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">AS </span><span style="color:#89DDFF;">{</span><span style="color:#A6ACCD;">attribute</span><span style="color:#89DDFF;">}]</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">TEXT </span><span style="color:#89DDFF;">|</span><span style="color:#A6ACCD;"> NUMERIC </span><span style="color:#89DDFF;">|</span><span style="color:#A6ACCD;"> GEO </span><span style="color:#89DDFF;">|</span><span style="color:#A6ACCD;"> TAG </span><span style="color:#89DDFF;">]</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">CASESENSITIVE</span><span style="color:#89DDFF;">]</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">SORTABLE</span><span style="color:#89DDFF;">]</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">[</span><span style="color:#A6ACCD;">NOINDEX</span><span style="color:#89DDFF;">]</span><span style="color:#A6ACCD;">] </span><span style="color:#89DDFF;">...</span></span></code></pre></div><ul><li><p>使用<code>FT.CREATE</code>命令可以建立索引，语法中的参数意义如下；</p></li><li><ul><li>index：索引名称；</li><li>data_type：建立索引的数据类型，目前支持JSON或者HASH两种；</li><li>PREFIX：通过它可以选择需要建立索引的数据前缀，比如<code>PREFIX 1 &quot;product:&quot;</code>表示为键中以<code>product:</code>为前缀的数据建立索引；</li><li>LANGUAGE：指定TEXT类型属性的默认语言，使用chinese可以设置为中文；</li><li>identifier：指定属性名称；</li><li>attribute：指定属性别名；</li><li>TEXT | NUMERIC | GEO | TAG：这些都是属性可选的类型；</li><li>SORTABLE：指定属性可以进行排序。</li></ul></li><li><p>看了语法可能不太好理解，直接对之前的商品数据建立索引试试就懂了；</p></li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">FT</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">CREATE productIdx ON JSON PREFIX </span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">product:</span><span style="color:#89DDFF;">&quot;</span><span style="color:#A6ACCD;"> LANGUAGE chinese SCHEMA $</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">id AS id NUMERIC $</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">name AS name TEXT $</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">subTitle AS subTitle TEXT $</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">price AS price NUMERIC SORTABLE $</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">brandName AS brandName TAG</span></span></code></pre></div><ul><li>建立完索引后，我们就可以使用<code>FT.SEARCH</code>对数据进行查看了，比如使用<code>*</code>可以查询全部；</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">FT</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">SEARCH productIdx </span><span style="color:#89DDFF;">*</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.5.13/202205131722098.png" alt="image-20220513172223009" style="zoom:67%;"><ul><li>由于我们设置了<code>price</code>字段为<code>SORTABLE</code>，我们可以以<code>price</code>降序返回商品信息；</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">FT</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">SEARCH productIdx </span><span style="color:#89DDFF;">*</span><span style="color:#A6ACCD;"> SORTBY price DESC</span></span></code></pre></div><ul><li>还可以指定返回的字段；</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">FT</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">SEARCH productIdx </span><span style="color:#89DDFF;">*</span><span style="color:#A6ACCD;"> RETURN </span><span style="color:#F78C6C;">3</span><span style="color:#A6ACCD;"> name subTitle price</span></span></code></pre></div><ul><li>我们把<code>brandName</code>设置为了<code>TAG</code>类型，我们可以使用如下语句查询品牌为<code>小米</code>或<code>苹果</code>的商品；</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">FT</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">SEARCH productIdx </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">@brandName:{小米 | 苹果}</span><span style="color:#89DDFF;">&#39;</span></span></code></pre></div><ul><li>我们把<code>brandName</code>设置为了<code>TAG</code>类型，我们可以使用如下语句查询品牌为<code>小米</code>或<code>苹果</code>的商品；</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">FT</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">SEARCH productIdx </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">@brandName:{小米 | 苹果}</span><span style="color:#89DDFF;">&#39;</span></span></code></pre></div><ul><li>我们把<code>brandName</code>设置为了<code>TAG</code>类型，我们可以使用如下语句查询品牌为<code>小米</code>或<code>苹果</code>的商品；</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">FT</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">SEARCH productIdx </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">@brandName:{小米 | 苹果}</span><span style="color:#89DDFF;">&#39;</span></span></code></pre></div><ul><li>在<code>FT.SEARCH</code>中直接指定搜索关键词，可以对所有<code>TEXT</code>类型的属性进行全局搜索，支持中文搜索，比如我们搜索下包含<code>黑色</code>字段的商品；</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">FT</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">SEARCH productIdx </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">黑色</span><span style="color:#89DDFF;">&#39;</span></span></code></pre></div><ul><li>当然我们也可以指定搜索的字段，比如搜索副标题中带有<code>红色</code>字段的商品；</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">FT</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">SEARCH productIdx </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">@subTitle:红色</span><span style="color:#89DDFF;">&#39;</span></span></code></pre></div><ul><li>通过<code>FT.DROPINDEX</code>命令可以删除索引，如果加入<code>DD</code>选项的话，会连数据一起删除；</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">FT</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">DROPINDEX productIdx</span></span></code></pre></div><ul><li>通过<code>FT.INFO</code>命令可以查看索引状态；</li></ul><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">FT</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">INFO productIdx</span></span></code></pre></div><ul><li><p>RediSearch的搜索语法比较复杂，不过我们可以对比SQL来使用它，具体可以参考下表。</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.5.13/202205131725865.png" alt="image-20220513172508778" style="zoom:80%;"></li></ul><h2 id="对比elasticsearch" tabindex="-1">对比Elasticsearch <a class="header-anchor" href="#对比elasticsearch" aria-label="Permalink to &quot;对比Elasticsearch&quot;">​</a></h2><blockquote><p>Redis官方曾公布了RediSearch与Elasticsearch的性能对比测试，大家可以看下。</p></blockquote><h3 id="索引能力" tabindex="-1">索引能力 <a class="header-anchor" href="#索引能力" aria-label="Permalink to &quot;索引能力&quot;">​</a></h3><p>对Wikipedia的560万（5.3GB）文档进行索引，RediSearch耗时<code>221s</code>，Elasticsearch耗时<code>349s</code>，RediSearch快了<code>58%</code>！</p><h3 id="查询能力" tabindex="-1">查询能力 <a class="header-anchor" href="#查询能力" aria-label="Permalink to &quot;查询能力&quot;">​</a></h3><p>数据建立索引后，使用32个客户端对两个单词进行检索，RediSearch的吞吐量达到<code>12.5K ops/sec</code>，Elasticsearch的吞吐量为<code>3.1K ops/sec</code>，RediSearch比Elasticsearch要快<code>4倍</code>。同时RediSearch的延迟为<code>8ms</code>，而Elasticsearch为<code>10ms</code>，RediSearch延迟稍微低些！</p><h1 id="发布和订阅" tabindex="-1">发布和订阅 <a class="header-anchor" href="#发布和订阅" aria-label="Permalink to &quot;发布和订阅&quot;">​</a></h1><h2 id="发布和订阅介绍" tabindex="-1">发布和订阅介绍 <a class="header-anchor" href="#发布和订阅介绍" aria-label="Permalink to &quot;发布和订阅介绍&quot;">​</a></h2><blockquote><p>Redis 发布订阅 (pub/sub) 是一种消息通信模式：<strong>发送者 (pub) 发送消息，订阅者 (sub) 接收消息</strong>。Redis 客户端可<strong>以订阅任意数量的频道</strong>。Redis 提供了基于「发布/订阅」模式的消息机制，在这种模式下，消息发布者与订阅者不需要进行直接通信。</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.8.30/202208131618206.png" alt="image-20220813161813082" style="zoom:50%;"><blockquote><p>如上图所示，消息发布者只需要想指定的频道发布消息，订阅该频道的每个客户端都可以接受到到这个消息。</p></blockquote><blockquote><p>使用 Redis 发布订阅这种机制，对于上面业务，下单支付业务只需要向**「支付结果」<strong>这个频道发送消息，其他下游业务订阅</strong>「支付结果」**这个频道，就能收相应消息，然后做出业务处理即可。</p></blockquote><p>Redis 中提供了一组命令，可以用于发布消息，订阅频道，取消订阅以及按照模式订阅。</p><blockquote><ul><li>使用频道（Channel）的发布订阅；</li><li>使用模式（Pattern）的发布订阅。</li></ul></blockquote><p><strong>需要注意的是，发布订阅机制与 db 空间无关，比如在 db 10 发布， db0 的订阅者也会收到消息。</strong></p><h2 id="频道-channel-的发布订阅" tabindex="-1">频道（Channel）的发布订阅 <a class="header-anchor" href="#频道-channel-的发布订阅" aria-label="Permalink to &quot;频道（Channel）的发布订阅&quot;">​</a></h2><blockquote><p>订阅者订阅频道；发布者向「频道」发布消息；所有订阅「<strong>频道</strong>」的订阅者收到消息。</p></blockquote><p>客户端订阅频道，任意进入一个CMD</p><div class="language-assembly"><button title="Copy Code" class="copy"></button><span class="lang">assembly</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;"># 可以只订阅一个频道</span></span>
<span class="line"><span style="color:#A6ACCD;">subscribe chan1</span></span>
<span class="line"><span style="color:#A6ACCD;"># 可以订阅多个频道</span></span>
<span class="line"><span style="color:#A6ACCD;">subscribe chan1 chan2 chan3</span></span></code></pre></div><p>其他客户端发布，发布完成内容在订阅客户端查看，进入另一个CMD</p><div class="language-assembly"><button title="Copy Code" class="copy"></button><span class="lang">assembly</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">publish chan1 hello</span></span>
<span class="line"><span style="color:#A6ACCD;">publish chan1 renshuo</span></span>
<span class="line"><span style="color:#A6ACCD;">publish chan2 renshuo1</span></span>
<span class="line"><span style="color:#A6ACCD;">publish chan3 renshuo3</span></span></code></pre></div><p>注：<strong>发布的消息没有持久化，如果在订阅的客户端收不到hello，只能收到订阅后发布的消息</strong></p><h2 id="模式-pattern-的发布订阅" tabindex="-1">模式（Pattern）的发布订阅 <a class="header-anchor" href="#模式-pattern-的发布订阅" aria-label="Permalink to &quot;模式（Pattern）的发布订阅&quot;">​</a></h2><blockquote><p>Redis 还支持模式匹配的订阅方式。简单来说，客户端可以订阅一个带 <code>*</code> 号的模式，如果某些频道的名字与这个模式匹配，那么当其他客户端发送给消息给这些频道时，订阅这个模式的客户端也将会到收到消息。使用 Redis 订阅模式，我们需要使用一个新的指令 <strong>「psubscribe」</strong>。</p></blockquote><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;"># 订阅和取消订阅</span></span>
<span class="line"><span style="color:#FFCB6B;">psubscribe</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">pay.</span><span style="color:#A6ACCD;">*</span></span>
<span class="line"><span style="color:#FFCB6B;">punsubscribe</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">pay.</span><span style="color:#A6ACCD;">*</span></span></code></pre></div><p>案例演示</p><p>订阅模式的指令是<code>PSUBSCRIBE</code>，如下表示 LSP 订阅「smile.girl.*」模式：</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">psubscribe</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">smile.girls.</span><span style="color:#A6ACCD;">*</span></span></code></pre></div><p>对应的反向取消模式订阅的指令是<code>PUNSUBSCRIBE smile.girl.*</code>。</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">publish</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">smile.girls.Tina</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">hello,zhangsan</span></span>
<span class="line"><span style="color:#FFCB6B;">publish</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">smile.girls.Lucy</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">hello,zhangsan1</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301241152192.png" alt="image-20230124115233054" style="zoom:67%;"><blockquote><p>需要注意的是，<strong>如果一个客户端订阅了与模式匹配的模式和频道，那么客户端会收到多次消息。</strong></p></blockquote><h1 id="事务-锁-秒杀" tabindex="-1">事务+锁+秒杀 <a class="header-anchor" href="#事务-锁-秒杀" aria-label="Permalink to &quot;事务+锁+秒杀&quot;">​</a></h1><blockquote><p>Redis事务是一个单独的隔离操作：事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中，不会被其他客户端发送来的命令请求所打断。<strong>Redis事务的主要作用就是串联多个命令防止别的命令插队</strong></p></blockquote><blockquote><p>单独的隔离操作 ：事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中，不会被其他客户端发送来的命令请求所打断。</p></blockquote><blockquote><p>没有隔离级别的概念 ：队列中的命令没有提交之前都不会实际被执行，事务提交前任何指令都不会被实际执行</p></blockquote><blockquote><p>不保证原子性 ：事务中如果有一条命令执行失败，其后的命令仍然会被执行，没有回滚</p></blockquote><h2 id="multi、exec、discard" tabindex="-1">Multi、Exec、discard <a class="header-anchor" href="#multi、exec、discard" aria-label="Permalink to &quot;Multi、Exec、discard&quot;">​</a></h2><blockquote><p>从输入<strong>multi命令开始，<strong>输入的命令都会依次进入命令队列中，但不会执行，直到输入</strong>exec执行</strong>后，Redis会将之前的命令队列中的命令依次执行。组队的过程中可以通过<strong>discard来放弃组队</strong>。</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img/image-20210918104039098.png" alt="image-20210918104039098" style="zoom:67%;"><h3 id="正常组队" tabindex="-1">正常组队 <a class="header-anchor" href="#正常组队" aria-label="Permalink to &quot;正常组队&quot;">​</a></h3><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">multi</span></span>
<span class="line"><span style="color:#82AAFF;">set</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">k1</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">v1</span></span>
<span class="line"><span style="color:#82AAFF;">set</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">k2</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">v2</span></span>
<span class="line"><span style="color:#82AAFF;">exec</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301241549962.png" alt="image-20230124154959839" style="zoom:67%;"><h3 id="放弃组队" tabindex="-1">放弃组队 <a class="header-anchor" href="#放弃组队" aria-label="Permalink to &quot;放弃组队&quot;">​</a></h3><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">multi</span></span>
<span class="line"><span style="color:#82AAFF;">set</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">a1</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">v1</span></span>
<span class="line"><span style="color:#82AAFF;">set</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">a2</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">v2</span></span>
<span class="line"><span style="color:#FFCB6B;">discard</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301241551562.png" alt="image-20230124155128417" style="zoom:67%;"><h2 id="事务的错误处理" tabindex="-1">事务的错误处理 <a class="header-anchor" href="#事务的错误处理" aria-label="Permalink to &quot;事务的错误处理&quot;">​</a></h2><blockquote><p>组队中某个命令出现了报告错误，执行时整个的所有队列都会被取消。</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img/image-20210918105105018.png" alt="image-20210918105105018" style="zoom:67%;"><blockquote><p>如果执行阶段某个命令报出了错误，则只有报错的命令不会被执行，而其他的命令都会执行，不会回滚。</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img/image-20210918105125524.png" alt="image-20210918105125524" style="zoom:67%;"><h2 id="悲观锁-乐观锁" tabindex="-1">悲观锁 &amp; 乐观锁 <a class="header-anchor" href="#悲观锁-乐观锁" aria-label="Permalink to &quot;悲观锁 &amp; 乐观锁&quot;">​</a></h2><h3 id="悲观锁" tabindex="-1">悲观锁 <a class="header-anchor" href="#悲观锁" aria-label="Permalink to &quot;悲观锁&quot;">​</a></h3><blockquote><p><strong>悲观锁(Pessimistic Lock)</strong>, 顾名思义，就是很悲观，每次去拿数据的时候都认为别人会修改，所以每次在拿数据的时候都会上锁，这样别人想拿这个数据就会block直到它拿到锁。<strong>传统的关系型数据库里边就用到了很多这种锁机制，比如行锁，表锁等，读锁，写锁等</strong>，都是在做操作之前先上锁。</p></blockquote><h3 id="乐观锁" tabindex="-1">乐观锁 <a class="header-anchor" href="#乐观锁" aria-label="Permalink to &quot;乐观锁&quot;">​</a></h3><blockquote><p><strong>乐观锁(Optimistic Lock),</strong> 顾名思义，就是很乐观，每次去拿数据的时候都认为别人不会修改，所以不会上锁，但是在更新的时候会判断一下在此期间别人有没有去更新这个数据，可以使用版本号等机制。<strong>乐观锁适用于多读的应用类型，这样可以提高吞吐量</strong>。Redis就是利用这种check-and-set机制实现事务的。<strong>抢票就是典型的乐观锁，好多人去抢一张票，最后只有一个人会支付成功。</strong></p></blockquote><h3 id="redis-乐观锁" tabindex="-1">Redis+乐观锁 <a class="header-anchor" href="#redis-乐观锁" aria-label="Permalink to &quot;Redis+乐观锁&quot;">​</a></h3><p>在执行multi之前，先执行watch key1 [key2],<strong>可以监视一个(或多个) key</strong> ，**如果在事务执行之前这个(或这些) **</p><div class="language-assembly"><button title="Copy Code" class="copy"></button><span class="lang">assembly</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">watch key [key ...]</span></span></code></pre></div><p><strong>key 被其他命令所改动，那么事务将被打断</strong>。</p><div class="language-assembly"><button title="Copy Code" class="copy"></button><span class="lang">assembly</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">unwatch</span></span></code></pre></div><p>取消 watch命令对所有 key 的监视。 如果在执行 WATCH 命令之后，EXEC 命令或DISCARD 命令先被执行了的话，那么就不需要再执行UNWATCH 了。</p><p>乐观锁的使用</p><p>客户端1</p><div class="language-assembly"><button title="Copy Code" class="copy"></button><span class="lang">assembly</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">-- 注意：balance还没被创建</span></span>
<span class="line"><span style="color:#A6ACCD;">watch balance</span></span>
<span class="line"><span style="color:#A6ACCD;">multi</span></span>
<span class="line"><span style="color:#A6ACCD;">incrby balance 10</span></span>
<span class="line"><span style="color:#A6ACCD;">exec</span></span></code></pre></div><p>客户端2</p><div class="language-assembly"><button title="Copy Code" class="copy"></button><span class="lang">assembly</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">set balance 10</span></span>
<span class="line"><span style="color:#A6ACCD;">watch balance</span></span>
<span class="line"><span style="color:#A6ACCD;">multi</span></span>
<span class="line"><span style="color:#A6ACCD;">incrby balance 10</span></span>
<span class="line"><span style="color:#A6ACCD;">exec</span></span></code></pre></div><p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img/image-20210918110254328.png" alt="image-20210918110254328"></p><h2 id="redis秒杀方案" tabindex="-1">Redis秒杀方案 <a class="header-anchor" href="#redis秒杀方案" aria-label="Permalink to &quot;Redis秒杀方案&quot;">​</a></h2><p>解决计数器和人员记录的事务操作，执行过程：库存-1，成功者+1</p><blockquote><ul><li><strong>加事务-乐观锁(解决超卖),但出现遗留库存和连接超时</strong></li><li><strong>加连接池解决超时问题</strong></li><li><strong>解决库存依赖问题，LUA脚本</strong></li></ul></blockquote><h1 id="经典问题⭐" tabindex="-1">经典问题⭐ <a class="header-anchor" href="#经典问题⭐" aria-label="Permalink to &quot;经典问题⭐&quot;">​</a></h1><h2 id="缓存穿透" tabindex="-1">缓存穿透 <a class="header-anchor" href="#缓存穿透" aria-label="Permalink to &quot;缓存穿透&quot;">​</a></h2><h3 id="问题描述" tabindex="-1">问题描述 <a class="header-anchor" href="#问题描述" aria-label="Permalink to &quot;问题描述&quot;">​</a></h3><blockquote><p>key对应的数据在数据源并不存在，每次针对此key的请求从缓存获取不到，请求都会压到数据源，从而可能压垮数据源。比如用一个不存在的用户id获取用户信息，不论缓存还是数据库都没有，若黑客利用此漏洞进行攻击可能压垮数据库。**即数据不存在 Redis 也不存在于数据库。**如果此时穿透了缓存，而直接数据库的请求数量非常多，数据库可能因为扛不住压力而挂掉。呜呜呜。</p></blockquote><blockquote><p>一个一定不存在缓存及查询不到的数据，由于缓存是不命中时被动写的，并且出于容错考虑，如果从存储层查不到数据则不写入缓存，这将导致这个不存在的数据每次请求都要到存储层去查询，失去了缓存的意义。</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301242221200.png" alt="image-20230124222154024" style="zoom:50%;"><h3 id="解决方案" tabindex="-1">解决方案 <a class="header-anchor" href="#解决方案" aria-label="Permalink to &quot;解决方案&quot;">​</a></h3><h4 id="缓存空值-⭐" tabindex="-1">缓存空值 ⭐ <a class="header-anchor" href="#缓存空值-⭐" aria-label="Permalink to &quot;缓存空值 ⭐&quot;">​</a></h4><blockquote><p>**对空值缓存：**如果一个查询返回的数据为空（不管是数据是否不存在），我们仍然把这个空结果（null）进行缓存，设置空结果的过期时间会很短，最长不超过五分钟</p></blockquote><p>所以，通常情况下，我们很少用布隆过滤器解决缓存穿透问题。其实，还有另外一种更简单的方案，即：<code>缓存空值</code>。</p><blockquote><p>当某个用户id在缓存中查不到，在数据库中也查不到时，也需要将该用户id缓存起来，只不过值是空的。这样后面的请求，再拿相同的用户id发起请求时，就能从缓存中获取空数据，直接返回了，而无需再去查一次数据库。</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.30/202207072331633.png" alt="image-20220707233120501" style="zoom:33%;"><p>关键点是不管从数据库有没有查到数据，都将结果放入缓存中，只是如果没有查到数据，缓存中的值是空的罢了。</p><p>这时候，我们就会问了呀 ~，如果大量不存在的请求过来，那么这时候缓存岂不是会缓存许多空对象了吗~~~</p><blockquote><p>没错哦！这也是使用缓存空对象会导致的一个问题：如果时间一长这样会导致缓存中存在大量空对象，这样不仅会占用许多的内存空间，还会浪费许多资源呀！。那这有没有什么可以解决的方法呢？我们来想一想：我们可以将这些对象在一段时间之后清理下不久可以了吗 ~</p></blockquote><p>嗯嗯，没错！在想想 Redis 里是不是给我们提供了有关过期时间的命令呀(<em>^▽^</em>)，这样我们可以在设置空对象的时间，顺便设置一个过期时间，就可以解决个问题了呀！</p><div class="language-c"><button title="Copy Code" class="copy"></button><span class="lang">c</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">setex key seconds valule:</span><span style="color:#676E95;font-style:italic;">//设置键值对的同时指定过期时间(s)</span></span></code></pre></div><p>在Java 中直接调用 API 操作即可：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">redisCache</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">put</span><span style="color:#A6ACCD;">(Integer</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">toString</span><span style="color:#A6ACCD;">(id)</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">null,</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">60</span><span style="color:#A6ACCD;">) </span><span style="color:#676E95;font-style:italic;">//过期时间为 60s</span></span></code></pre></div><h4 id="设置白名单" tabindex="-1">设置白名单 <a class="header-anchor" href="#设置白名单" aria-label="Permalink to &quot;设置白名单&quot;">​</a></h4><blockquote><p>**设置可访问的名单（白名单）：**使用bitmaps类型定义一个可以访问的名单，名单id作为bitmaps的偏移量，每次访问和bitmap里面的id进行比较，如果访问id不在bitmaps里面，进行拦截，不允许访问。</p></blockquote><h4 id="布隆过滤器" tabindex="-1">布隆过滤器 <a class="header-anchor" href="#布隆过滤器" aria-label="Permalink to &quot;布隆过滤器&quot;">​</a></h4><blockquote><p><strong>采用布隆过滤器</strong>：(布隆过滤器（Bloom Filter）是1970年由布隆提出的。它实际上是一个很长的二进制向量(位图)和一系列随机映射函数（哈希函数）。</p></blockquote><blockquote><p>布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法，缺点是有一定的误识别率和删除困难。)将所有可能存在的数据哈希到一个足够大的bitmaps中，一个一定不存在的数据会被 这个bitmaps拦截掉，从而避免了对底层存储系统的查询压力。</p></blockquote><h4 id="实时监控" tabindex="-1">实时监控 <a class="header-anchor" href="#实时监控" aria-label="Permalink to &quot;实时监控&quot;">​</a></h4><blockquote><p>**进行实时监控：**当发现Redis的命中率开始急速降低，需要排查访问对象和访问的数据，和运维人员配合，可以设置黑名单限制服务</p></blockquote><h2 id="缓存击穿" tabindex="-1">缓存击穿 <a class="header-anchor" href="#缓存击穿" aria-label="Permalink to &quot;缓存击穿&quot;">​</a></h2><h3 id="问题描述-1" tabindex="-1">问题描述 <a class="header-anchor" href="#问题描述-1" aria-label="Permalink to &quot;问题描述&quot;">​</a></h3><blockquote><p>缓存击穿是指有某个key经常被查询，经常被用户特殊关怀，也就类比“熟客” 或者 一个key经常不被访问。但是这时候，如果这个key在缓存的过期时间失效的时候或者这是个冷门key时，这时候突然有大量有关这个key的访问请求，这样会导致大并发请求直接穿透缓存，请求数据库，瞬间对数据库的访问压力增大。</p><p><strong>单一热点数据、高并发、数据失效</strong></p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.30/202207072333034.png" alt="image-20220707233319903" style="zoom:50%;"><h3 id="解决方案-1" tabindex="-1">解决方案 <a class="header-anchor" href="#解决方案-1" aria-label="Permalink to &quot;解决方案&quot;">​</a></h3><h4 id="加锁" tabindex="-1">加锁 <a class="header-anchor" href="#加锁" aria-label="Permalink to &quot;加锁&quot;">​</a></h4><blockquote><p>数据库压力过大的根源是，因为同一时刻太多的请求访问了数据库。如果我们能够限制，同一时刻只有一个请求才能访问某个productId的数据库商品信息，不就能解决问题了？</p></blockquote><p>答：没错，我们可以用<code>加锁</code>的方式，实现上面的功能。</p><blockquote><p>对于缓存击穿的问题：我们常用的解决方案是加锁。对于key过期的时候，当key要查询数据库的时候加上一把锁，这时只能让第一个请求进行查询数据库，然后把从数据库中查询到的值存储到缓存中，对于剩下的相同的key，可以直接从缓存中获取即可。</p></blockquote><blockquote><p>如果我们是在单机环境下：直接使用常用的锁即可（如：Lock、Synchronized等），在分布式环境下我们可以使用分布式锁，如：基于数据库、基于Redis或者zookeeper 的分布式锁。</p></blockquote><blockquote><p>在访问数据库时加锁，防止多个相同productId的请求同时访问数据库。然后，数据库中查询到的结果，放入缓存</p></blockquote><h4 id="自动续期" tabindex="-1">自动续期 <a class="header-anchor" href="#自动续期" aria-label="Permalink to &quot;自动续期&quot;">​</a></h4><p>出现缓存击穿问题是由于key过期了导致的。那么，我们换一种思路，在key快要过期之前，就自动给它续期，不就OK了？</p><p>答：没错，我们可以用job给指定key自动续期。</p><p>比如说，我们有个分类功能，设置的缓存过期时间是30分钟。但有个job每隔20分钟执行一次，自动更新缓存，重新设置过期时间为30分钟。</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.30/202207072334439.png" alt="image-20220707233427301" style="zoom:50%;"><p>这样就能保证，分类缓存不会失效。</p><p>此外，在很多请求第三方平台接口时，我们往往需要先调用一个获取token的接口，然后用这个token作为参数，请求真正的业务接口。一般获取到的token是有有效期的，比如24小时之后失效。</p><p>如果我们每次请求对方的业务接口，都要先调用一次获取token接口，显然比较麻烦，而且性能不太好。</p><p>这时候，我们可以把第一次获取到的token缓存起来，请求对方业务接口时从缓存中获取token。</p><p>同时，有一个job每隔一段时间，比如每隔12个小时请求一次获取token接口，不停刷新token，重新设置token的过期时间。</p><h4 id="缓存不失效" tabindex="-1">缓存不失效 <a class="header-anchor" href="#缓存不失效" aria-label="Permalink to &quot;缓存不失效&quot;">​</a></h4><blockquote><p>此外，对于很多热门key，其实是可以不用设置过期时间，让其永久有效的。比如参与秒杀活动的热门商品，由于这类商品id并不多，在缓存中我们可以不设置过期时间。</p></blockquote><blockquote><p>在秒杀活动开始前，我们先用一个程序提前从数据库中查询出商品的数据，然后同步到缓存中，提前做<strong>预热</strong>。等秒杀活动结束一段时间之后，我们再<strong>手动删除</strong>这些无用的缓存即可。</p></blockquote><h4 id="过期时间-随机值" tabindex="-1">过期时间 + 随机值 <a class="header-anchor" href="#过期时间-随机值" aria-label="Permalink to &quot;过期时间 + 随机值&quot;">​</a></h4><blockquote><p>对于热点数据，我们不设置过期时间，这样就可以把请求都放在缓存中处理，充分把 Redis 高吞吐量性能利用起来。或者过期时间再加一个随机值。</p></blockquote><p>设计缓存的过期时间时，使用公式：<strong>过期时间=base 时间+随机时间</strong>。</p><blockquote><p>即相同业务数据写缓存时，在基础过期时间之上，再加一个随机的过期时间，让数据在未来一段时间内慢慢过期，避免瞬时全部过期，对 DB 造成过大压力</p></blockquote><h4 id="缓存预热-⭐" tabindex="-1">缓存预热 ⭐ <a class="header-anchor" href="#缓存预热-⭐" aria-label="Permalink to &quot;缓存预热 ⭐&quot;">​</a></h4><blockquote><p>预先把热门数据提前存入 Redis 中，并设热门数据的过期时间超大值。key可能会在某些时间点被超高并发地访问，是一种非常“热点”的数据。这个时候，需要考虑一个问题：缓存被“击穿”的问题。</p></blockquote><h2 id="缓存雪崩" tabindex="-1">缓存雪崩 <a class="header-anchor" href="#缓存雪崩" aria-label="Permalink to &quot;缓存雪崩&quot;">​</a></h2><blockquote><p><strong>数据保存在缓存系统并设置了过期时间，但是由于在同时一刻，大量数据同时过期。系统就把请求全部打到数据库获取数据，并发量大的话就会导致数据库压力激增</strong>。而缓存雪崩是缓存击穿的升级版，缓存击穿说的是某一个热门key失效了，而缓存雪崩说的是有多个热门key同时失效。看起来，如果发生缓存雪崩，问题更严重。</p></blockquote><blockquote><p><strong>缓存雪崩是发生在大量数据同时失效的场景，而缓存击穿是在某个热点数据失效的场景，这是最大区别</strong></p></blockquote><h3 id="问题描述-2" tabindex="-1">问题描述 <a class="header-anchor" href="#问题描述-2" aria-label="Permalink to &quot;问题描述&quot;">​</a></h3><blockquote><p>由于后台小哥预热缓存的时候将所有商品的缓存时间都设置为2小时过期，所有的商品在同一个时间点全部失效，瞬间所有的请求都落在数据库上，导致数据库扛不住压力崩溃，用户所有的请求都超时报错。</p></blockquote><p>缓存雪崩目前有两种：</p><blockquote><p>有大量的热门缓存，同时失效。会导致大量的请求，访问数据库。而数据库很有可能因为扛不住压力，而直接挂掉</p><p>缓存服务器down机了，可能是机器硬件问题，或者机房网络问题。总之，造成了整个缓存的不可用</p></blockquote><p>归根结底都是有大量的请求，透过缓存，而直接访问数据库了。</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.30/202207072336915.png" alt="image-20220707233630773" style="zoom:45%;"><p>后台小哥通过日志定位排查发现问题后，进行了一系列操作：</p><blockquote><p>首先通过API Gateway（网关）限制大部分流量进来</p><p>接着将宕机的数据库服务重启</p><p>再重新预热缓存</p><p>确认缓存和数据库服务正常后将网关流量正常放开，大约01：30 抢购活动恢复正常。</p></blockquote><h3 id="解决方案-2" tabindex="-1">解决方案 <a class="header-anchor" href="#解决方案-2" aria-label="Permalink to &quot;解决方案&quot;">​</a></h3><h4 id="构建多级缓存" tabindex="-1">构建多级缓存 <a class="header-anchor" href="#构建多级缓存" aria-label="Permalink to &quot;构建多级缓存&quot;">​</a></h4><blockquote><p>nginx缓存 + redis缓存 +其他缓存（ehcache等）</p></blockquote><h4 id="互斥锁" tabindex="-1">互斥锁 <a class="header-anchor" href="#互斥锁" aria-label="Permalink to &quot;互斥锁&quot;">​</a></h4><blockquote><p>跟缓存击穿解决思路一致，同一时间只让一个线程构建缓存，其他线程阻塞排队。用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写，从而避免失效时大量的并发请求落到底层存储系统上。不适用高并发情况</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.30/202207072323908.png" alt="image-20220707232325808" style="zoom:50%;"><h4 id="均匀过期" tabindex="-1">均匀过期 <a class="header-anchor" href="#均匀过期" aria-label="Permalink to &quot;均匀过期&quot;">​</a></h4><blockquote><p>比如我们可以在原有的失效时间基础上增加一个随机值，比如1-5分钟随机，这样每一个缓存的过期时间的重复率就会降低，就很难引发集体失效的事件。设置不同的过期时间，让缓存失效的时间点尽量均匀。通常可以为有效期增加随机值或者统一规划有效期。</p></blockquote><blockquote><p>为了解决缓存雪崩问题，我们首先要尽量避免缓存同时失效的情况发生。这就要求我们不要设置相同的过期时间。</p></blockquote><blockquote><p>可以在设置的过期时间基础上，再加个1~60秒的随机数。实际过期时间 = 过期时间 + 1~60秒的随机数</p></blockquote><p>这样即使在高并发的情况下，多个请求同时设置过期时间，由于有随机数的存在，也不会出现太多相同的过期key。</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.30/202207072323511.png" alt="image-20220707232305404" style="zoom:67%;"><h4 id="缓存永不过期" tabindex="-1">缓存永不过期 <a class="header-anchor" href="#缓存永不过期" aria-label="Permalink to &quot;缓存永不过期&quot;">​</a></h4><blockquote><p>跟缓存击穿解决思路一致，缓存在物理上永远不过期，用一个异步的线程更新缓存。</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.30/202207072324890.png" alt="image-20220707232449779" style="zoom:50%;"><h4 id="高可用" tabindex="-1">高可用 <a class="header-anchor" href="#高可用" aria-label="Permalink to &quot;高可用&quot;">​</a></h4><blockquote><p>针对缓存服务器down机的情况，在前期做系统设计时，可以做一些高可用架构。比如：如果使用了redis，可以使用哨兵模式，或者集群模式，避免出现单节点故障导致整个redis服务不可用的情况。</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.30/202207072340552.png" alt="image-20220707234011398" style="zoom:40%;"><blockquote><p>使用哨兵模式之后，当某个master服务下线时，自动将该master下的某个slave服务升级为master服务，替代已下线的master服务继续处理请求。</p></blockquote><h4 id="服务降级" tabindex="-1">服务降级 <a class="header-anchor" href="#服务降级" aria-label="Permalink to &quot;服务降级&quot;">​</a></h4><blockquote><p>如果做了高可用架构，redis服务还是挂了，该怎么办呢？这时候，就需要做服务降级了。</p></blockquote><blockquote><p>我们需要配置一些默认的兜底数据。程序中有个全局开关，比如有10个请求在最近一分钟内，从redis中获取数据失败，则全局开关打开。后面的新请求，就直接从配置中心中获取默认的数据。</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.30/202207072339944.png" alt="image-20220707233952815" style="zoom:33%;"><blockquote><p>当然，还需要有个job，每隔一定时间去从redis中获取数据，如果在最近一分钟内可以获取到两次数据（这个参数可以自己定），则把全局开关关闭。后面来的请求，又可以正常从redis中获取数据了。需要特别说一句，该方案并非所有的场景都适用，需要根据实际业务场景决定。</p></blockquote><h4 id="接口限流" tabindex="-1">接口限流 <a class="header-anchor" href="#接口限流" aria-label="Permalink to &quot;接口限流&quot;">​</a></h4><blockquote><p>当访问的不是核心数据的时候，在查询的方法上加上<strong>接口限流保护</strong>。比如设置 10000 req/s。</p></blockquote><blockquote><p>如果访问的是核心数据接口，缓存不存在允许从数据库中查询并设置到缓存中。这样的话，只有部分请求会发送到数据库，减少了压力。限流，就是指，我们在<strong>业务系统的请求入口前端控制每秒进入系统的请求数，避免过多的请求被发送到数据库。</strong></p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.8.30/202208151627282.png" alt="image-20220815162728163" style="zoom:33%;"><h2 id="缓存热点" tabindex="-1">缓存热点 <a class="header-anchor" href="#缓存热点" aria-label="Permalink to &quot;缓存热点&quot;">​</a></h2><blockquote><p>对于突发事件，大量用户同时去访问热点信息，这个突发热点信息所在的缓存节点就很容易出现过载和卡顿现象，甚至 Crash，我们称之为缓存热点。</p></blockquote><blockquote><p>这个在新浪微博经常遇到，某大V明星出轨、结婚、离婚，瞬间引发数百千万的吃瓜群众围观，访问同一个key，流量集中打在一个缓存节点机器，很容易打爆网卡、带宽、CPU的上限，最终导致缓存不可用。</p></blockquote><p><strong>解决方案：</strong></p><blockquote><ul><li>首先能先找到这个热key来，比如通过Spark实时流分析，及时发现新的热点key。</li><li>将集中化流量打散，避免一个缓存节点过载。由于只有一个key，我们可以在key的后面拼上有序编号，比如key#01、key#02、key#10多个副本，这些加工后的key位于多个缓存节点上。</li><li>每次请求时，客户端随机访问一个即可</li></ul></blockquote><blockquote><p>可以设计一个缓存服务治理管理后台，实时监控缓存的SLA，并打通分布式配置中心，对于一些<code>hot key</code>可以快速、动态扩容。</p></blockquote><h2 id="缓存大key" tabindex="-1">缓存大Key <a class="header-anchor" href="#缓存大key" aria-label="Permalink to &quot;缓存大Key&quot;">​</a></h2><blockquote><p>当访问缓存时，如果key对应的value过大，读写、加载很容易超时，容易引发网络拥堵。另外缓存的字段较多时，每个字段的变更都会引发缓存数据的变更，频繁的读写，导致慢查询。如果大key过期被缓存淘汰失效，预热数据要花费较多的时间，也会导致慢查询。</p></blockquote><blockquote><p>所以我们在设计缓存的时候，要注意缓存的粒度，既不能过大，如果过大很容易导致网络拥堵；也不能过小，如果太小，查询频率会很高，每次请求都要查询多次。</p></blockquote><p><strong>解决方案：</strong></p><blockquote><ul><li>方案一：设置一个阈值，当value的长度超过阈值时，对内容启动压缩，降低kv的大小</li><li>方案二：评估大key所占的比例，由于很多框架采用池化技术，如：Memcache，可以预先分配大对象空间。真正业务请求时，直接拿来即用。</li><li>方案三：颗粒划分，将大key拆分为多个小key，独立维护，成本会降低不少</li><li>方案四：大key要设置合理的过期时间，尽量不淘汰那些大key</li></ul></blockquote><h1 id="分布式锁-2" tabindex="-1">分布式锁 <a class="header-anchor" href="#分布式锁-2" aria-label="Permalink to &quot;分布式锁&quot;">​</a></h1><h2 id="分布式锁概述" tabindex="-1">分布式锁概述 <a class="header-anchor" href="#分布式锁概述" aria-label="Permalink to &quot;分布式锁概述&quot;">​</a></h2><blockquote><p>随着业务发展的需要，原单体单机部署的系统被演化成分布式集群系统后，由于分布式系统多线程、多进程并且分布在不同机器上，这将使原单机部署情况下的并发控制锁策略失效，单纯的Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问，这就是分布式锁要解决的问题！</p></blockquote><p>分布式锁主流的实现方案：</p><blockquote><p>基于数据库实现分布式锁、基于缓存（Redis等）、基于Zookeeper</p></blockquote><p>每一种分布式锁解决方案都有各自的优缺点：</p><blockquote><p>性能：redis最高、可靠性：zookeeper最高</p></blockquote><p>为了确保分布式锁可用，我们至少要确保锁的实现同时<strong>满足以下四个条件</strong>：</p><blockquote><p>- 互斥性。在任意时刻，只有一个客户端能持有锁。</p><p>- 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁，也能保证后续其他客户端能加锁。</p><p>- 解铃还须系铃人。加锁和解锁必须是同一个客户端，客户端自己不能把别人加的锁给解了。</p><p>- 加锁和解锁必须具有原子性。</p></blockquote><h2 id="redis-命令" tabindex="-1">Redis 命令 <a class="header-anchor" href="#redis-命令" aria-label="Permalink to &quot;Redis 命令&quot;">​</a></h2><div class="language-sql"><button title="Copy Code" class="copy"></button><span class="lang">sql</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">-- setnx上锁</span></span>
<span class="line"><span style="color:#A6ACCD;">setnx age </span><span style="color:#F78C6C;">11</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 再次设置，就会出错</span></span>
<span class="line"><span style="color:#A6ACCD;">setnx age </span><span style="color:#F78C6C;">22</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 除非删除该key，才能修改</span></span>
<span class="line"><span style="color:#A6ACCD;">del age</span></span></code></pre></div><p>改进版本：加上过期时间，nx：上锁，ex：加上过期时间</p><div class="language-assembly"><button title="Copy Code" class="copy"></button><span class="lang">assembly</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">set name1 maoyan nx ex 20</span></span></code></pre></div><h2 id="实战演练-2" tabindex="-1">实战演练 <a class="header-anchor" href="#实战演练-2" aria-label="Permalink to &quot;实战演练&quot;">​</a></h2><h3 id="基础实现" tabindex="-1">基础实现 <a class="header-anchor" href="#基础实现" aria-label="Permalink to &quot;基础实现&quot;">​</a></h3><div class="language-java"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">@</span><span style="color:#C792EA;">Resource</span></span>
<span class="line"><span style="color:#C792EA;">private</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">RedisTemplate</span><span style="color:#A6ACCD;"> redisTemplate</span><span style="color:#89DDFF;">;</span></span></code></pre></div><div class="language-java"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">@</span><span style="color:#C792EA;">GetMapping</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">testLock</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">)</span></span>
<span class="line"><span style="color:#C792EA;">public</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">void</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">testLock</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">//1获取锁，setne</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#C792EA;">Boolean</span><span style="color:#A6ACCD;"> lock </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> redisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">opsForValue</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">setIfAbsent</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">lock</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">111</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">//2获取锁成功、查询num的值</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;font-style:italic;">if</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">lock</span><span style="color:#89DDFF;">){</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#C792EA;">Object</span><span style="color:#A6ACCD;"> value </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> redisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">opsForValue</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">get</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">num</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">//2.1判断num为空</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;font-style:italic;">if</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">value </span><span style="color:#89DDFF;">==</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">null){</span></span>
<span class="line"><span style="color:#A6ACCD;">            redisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">opsForValue</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">set</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">num</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#A6ACCD;">           value </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> redisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">opsForValue</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">get</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">num</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">//2.2有值就转成成int</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#C792EA;">int</span><span style="color:#A6ACCD;"> num </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> Integer</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">parseInt</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">value</span><span style="color:#89DDFF;">+</span><span style="color:#89DDFF;">&quot;&quot;</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">//2.3把redis的num加1</span></span>
<span class="line"><span style="color:#A6ACCD;">        redisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">opsForValue</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">set</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">num</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">++</span><span style="color:#A6ACCD;">num</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">//2.4释放锁，del</span></span>
<span class="line"><span style="color:#A6ACCD;">        redisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">delete</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">lock</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">}</span><span style="color:#89DDFF;font-style:italic;">else</span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">//3获取锁失败、每隔0.1秒再获取</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;font-style:italic;">try</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">            Thread</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">sleep</span><span style="color:#89DDFF;">(</span><span style="color:#F78C6C;">100</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#82AAFF;">testLock</span><span style="color:#89DDFF;">();</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">catch</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">(</span><span style="color:#C792EA;">InterruptedException</span><span style="color:#A6ACCD;"> </span><span style="color:#A6ACCD;font-style:italic;">e</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">            e</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">printStackTrace</span><span style="color:#89DDFF;">();</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">ab </span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">c </span><span style="color:#F78C6C;">100</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">n </span><span style="color:#F78C6C;">1000</span><span style="color:#A6ACCD;"> http:</span><span style="color:#89DDFF;">//</span><span style="color:#F78C6C;">192.168.0.199</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">8080</span><span style="color:#89DDFF;">/</span><span style="color:#A6ACCD;">testLock</span></span></code></pre></div><blockquote><ul><li>-n 即requests，用于<strong>指定压力测试总共的执行次数</strong></li><li>-c 即concurrency，用于<strong>指定的并发数</strong></li></ul></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301241704596.png" alt="image-20230124170431447" style="zoom:67%;"><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301241704896.png" alt="image-20230124170409792" style="zoom:80%;"><h3 id="进阶版" tabindex="-1">进阶版 <a class="header-anchor" href="#进阶版" aria-label="Permalink to &quot;进阶版&quot;">​</a></h3><blockquote><p>设置过期时间有两种方式：</p><p>首先想到通过expire设置过期时间（缺乏原子性：如果在setnx和expire之间出现异常，锁也无法释放）</p><p>在set时指定过期时间（推荐）</p></blockquote><div class="language-java"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">@</span><span style="color:#C792EA;">GetMapping</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">testLock</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">)</span></span>
<span class="line"><span style="color:#C792EA;">public</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">void</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">testLock</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 优化之UUID防误删</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#C792EA;">String</span><span style="color:#A6ACCD;"> uuid </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> UUID</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">randomUUID</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">toString</span><span style="color:#89DDFF;">();</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 1、获取锁，setIfAbsent就是setnx,后面两个时间参数可选</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#C792EA;">Boolean</span><span style="color:#A6ACCD;"> lock </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> redisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">opsForValue</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">setIfAbsent</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">lock</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> uuid</span><span style="color:#89DDFF;">,</span><span style="color:#F78C6C;">3</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">            TimeUnit</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">SECONDS</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">//2获取锁成功、查询num的值</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;font-style:italic;">if</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">lock</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#C792EA;">Object</span><span style="color:#A6ACCD;"> value </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> redisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">opsForValue</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">get</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">num</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">//2.1判断num为空</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;font-style:italic;">if</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">value </span><span style="color:#89DDFF;">==</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">null){</span></span>
<span class="line"><span style="color:#A6ACCD;">            redisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">opsForValue</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">set</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">num</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#A6ACCD;">            value </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> redisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">opsForValue</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">get</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">num</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">//2.2有值就转成成int</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#C792EA;">int</span><span style="color:#A6ACCD;"> num </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> Integer</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">parseInt</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">value </span><span style="color:#89DDFF;">+</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;&quot;</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">//2.3把redis的num加1</span></span>
<span class="line"><span style="color:#A6ACCD;">        redisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">opsForValue</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">set</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">num</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">++</span><span style="color:#A6ACCD;">num</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">//2.4释放锁，del</span></span>
<span class="line"><span style="color:#A6ACCD;">        redisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">delete</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">lock</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">else</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">//3获取锁失败、每隔0.1秒再获取</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;font-style:italic;">try</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">            Thread</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">sleep</span><span style="color:#89DDFF;">(</span><span style="color:#F78C6C;">100</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#82AAFF;">testLock</span><span style="color:#89DDFF;">();</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">catch</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">(</span><span style="color:#C792EA;">InterruptedException</span><span style="color:#A6ACCD;"> </span><span style="color:#A6ACCD;font-style:italic;">e</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">            e</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">printStackTrace</span><span style="color:#89DDFF;">();</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">ab </span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">c </span><span style="color:#F78C6C;">100</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">n </span><span style="color:#F78C6C;">1000</span><span style="color:#A6ACCD;"> http:</span><span style="color:#89DDFF;">//</span><span style="color:#F78C6C;">192.168.0.199</span><span style="color:#A6ACCD;">:</span><span style="color:#F78C6C;">8080</span><span style="color:#89DDFF;">/</span><span style="color:#A6ACCD;">testLock</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301241709683.png" alt="image-20230124170914525" style="zoom:67%;"><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301241709041.png" alt="image-20230124170941932" style="zoom:67%;"><h3 id="终极版" tabindex="-1">终极版 <a class="header-anchor" href="#终极版" aria-label="Permalink to &quot;终极版&quot;">​</a></h3><div class="language-java"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">@</span><span style="color:#C792EA;">GetMapping</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">testLockLua</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">)</span></span>
<span class="line"><span style="color:#C792EA;">public</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">void</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">testLockLua</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">//1 声明一个uuid ,将做为一个value 放入我们的key所对应的值中</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#C792EA;">String</span><span style="color:#A6ACCD;"> uuid </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> UUID</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">randomUUID</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">toString</span><span style="color:#89DDFF;">();</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">//2 定义一个锁：lua 脚本可以使用同一把锁，来实现删除！</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#C792EA;">String</span><span style="color:#A6ACCD;"> skuId </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">25</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">;</span><span style="color:#A6ACCD;"> </span><span style="color:#676E95;font-style:italic;">// 访问skuId 为25号的商品 100008348542</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#C792EA;">String</span><span style="color:#A6ACCD;"> locKey </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">lock:</span><span style="color:#89DDFF;">&quot;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">+</span><span style="color:#A6ACCD;"> skuId</span><span style="color:#89DDFF;">;</span><span style="color:#A6ACCD;"> </span><span style="color:#676E95;font-style:italic;">// 锁住的是每个商品的数据</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 3 获取锁</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#C792EA;">Boolean</span><span style="color:#A6ACCD;"> lock </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> redisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">opsForValue</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">setIfAbsent</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">locKey</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> uuid</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">3</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> TimeUnit</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">SECONDS</span><span style="color:#89DDFF;">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 第一种： lock 与过期时间中间不写任何的代码。</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// redisTemplate.expire(&quot;lock&quot;,10, TimeUnit.SECONDS);//设置过期时间</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 如果true</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;font-style:italic;">if</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">lock</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">// 执行的业务逻辑开始</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">// 获取缓存中的num 数据</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#C792EA;">Object</span><span style="color:#A6ACCD;"> value </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> redisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">opsForValue</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">get</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">num</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">// 如果是空直接返回</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">//2.1判断num为空</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;font-style:italic;">if</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">value </span><span style="color:#89DDFF;">==</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">null){</span></span>
<span class="line"><span style="color:#A6ACCD;">            redisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">opsForValue</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">set</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">num</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#A6ACCD;">            value </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> redisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">opsForValue</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">get</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">num</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">// 不是空 如果说在这出现了异常！ 那么delete 就删除失败！ 也就是说锁永远存在！</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#C792EA;">int</span><span style="color:#A6ACCD;"> num </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> Integer</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">parseInt</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">value </span><span style="color:#89DDFF;">+</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;&quot;</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">// 使num 每次+1 放入缓存</span></span>
<span class="line"><span style="color:#A6ACCD;">        redisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">opsForValue</span><span style="color:#89DDFF;">().</span><span style="color:#82AAFF;">set</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">num</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> String</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">valueOf</span><span style="color:#89DDFF;">(++</span><span style="color:#A6ACCD;">num</span><span style="color:#89DDFF;">));</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#676E95;font-style:italic;">/*使用lua脚本来锁*/</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">// 定义lua 脚本</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#C792EA;">String</span><span style="color:#A6ACCD;"> script </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">if redis.call(&#39;get&#39;, KEYS[1]) == ARGV[1] then return </span><span style="color:#89DDFF;">&quot;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">+</span></span>
<span class="line"><span style="color:#A6ACCD;">                        </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">redis.call(&#39;del&#39;, KEYS[1]) else return 0 end</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">// 使用redis执行lua执行</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#C792EA;">DefaultRedisScript</span><span style="color:#89DDFF;">&lt;</span><span style="color:#C792EA;">Long</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> redisScript </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">new</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">DefaultRedisScript</span><span style="color:#89DDFF;">&lt;&gt;();</span></span>
<span class="line"><span style="color:#A6ACCD;">        redisScript</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">setScriptText</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">script</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">// 设置一下返回值类型 为Long</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">// 因为删除判断的时候，返回的0,给其封装为数据类型。如果不封装那么默认返回String 类型，</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">// 那么返回字符串与0 会有发生错误。</span></span>
<span class="line"><span style="color:#A6ACCD;">        redisScript</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">setResultType</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">Long</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">class</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">// 第一个要是script 脚本 ，第二个需要判断的key，第三个就是key所对应的值。</span></span>
<span class="line"><span style="color:#A6ACCD;">        redisTemplate</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">execute</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">redisScript</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> Arrays</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">asList</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">locKey</span><span style="color:#89DDFF;">),</span><span style="color:#A6ACCD;"> uuid</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">else</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">// 其他线程等待</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;font-style:italic;">try</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">            </span><span style="color:#676E95;font-style:italic;">// 睡眠</span></span>
<span class="line"><span style="color:#A6ACCD;">            Thread</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">sleep</span><span style="color:#89DDFF;">(</span><span style="color:#F78C6C;">100</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#89DDFF;">            </span><span style="color:#676E95;font-style:italic;">// 睡醒了之后，调用方法。</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#82AAFF;">testLockLua</span><span style="color:#89DDFF;">();</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">catch</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">(</span><span style="color:#C792EA;">InterruptedException</span><span style="color:#A6ACCD;"> </span><span style="color:#A6ACCD;font-style:italic;">e</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">            e</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">printStackTrace</span><span style="color:#89DDFF;">();</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">ab</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-c</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">100</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-n</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1000</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">http://192.168.0.199:8080/testLockLua</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301241719912.png" alt="image-20230124171906756" style="zoom:67%;"><h1 id="消息队列" tabindex="-1">消息队列 <a class="header-anchor" href="#消息队列" aria-label="Permalink to &quot;消息队列&quot;">​</a></h1><h2 id="消息队列概述" tabindex="-1">消息队列概述 <a class="header-anchor" href="#消息队列概述" aria-label="Permalink to &quot;消息队列概述&quot;">​</a></h2><p><strong>消息队列</strong>（<strong>M</strong>essage <strong>Q</strong>ueue），字面意思就是存放消息的队列。最简单的消息队列模型包括3个角色：</p><blockquote><ul><li>消息队列：存储和管理消息，也被称为消息代理（Message Broker）</li><li>生产者：发送消息到消息队列</li><li>消费者：从消息队列获取消息并处理消息</li></ul></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.5/202205061715375.png" alt="image-20220506171525306" style="zoom:80%;"><p>Redis提供了三种不同的方式来实现消息队列：</p><blockquote><ul><li>list结构：基于List结构模拟消息队列</li><li>PubSub：基本的点对点消息模型</li><li>Stream：比较完善的消息队列模型</li></ul></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.5/202205061716012.png" alt="image-20220506171612949" style="zoom:80%;"><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.6.17/image-20230623151649042.png" alt="image-20230623151649042" style="zoom:80%;"><h2 id="基于list的消息队列" tabindex="-1">基于List的消息队列 <a class="header-anchor" href="#基于list的消息队列" aria-label="Permalink to &quot;基于List的消息队列&quot;">​</a></h2><blockquote><p><strong>消息队列</strong>（<strong>M</strong>essage <strong>Q</strong>ueue），字面意思就是存放消息的队列。而Redis的list数据结构是一个双向链表，很容易模拟出队列效果。</p></blockquote><p>队列是入口和出口不在一边，因此我们可以利用：LPUSH 结合 RPOP、或者 RPUSH 结合 LPOP来实现。</p><blockquote><p>不过要注意的是，当队列中没有消息时RPOP或LPOP操作会返回null，并不像JVM的阻塞队列那样会阻塞并等待消息。因此这里应该使用<strong>BRPOP</strong>或者<strong>BLPOP</strong>来实现阻塞效果。</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.5/202205061717699.png" alt="image-20220506171710637" style="zoom:80%;"><blockquote><p>基于List的消息队列有哪些优缺点？</p></blockquote><p>优点：</p><blockquote><ul><li>利用Redis存储，不受限于JVM内存上限</li><li>基于Redis的持久化机制，数据安全性有保证</li><li>可以满足消息有序性</li></ul></blockquote><p>缺点：</p><blockquote><ul><li>无法避免消息丢失</li><li>只支持单消费者</li></ul></blockquote><h2 id="基于pubsub的消息队列" tabindex="-1">基于PubSub的消息队列 <a class="header-anchor" href="#基于pubsub的消息队列" aria-label="Permalink to &quot;基于PubSub的消息队列&quot;">​</a></h2><blockquote><p>**PubSub（发布订阅）**是Redis2.0版本引入的消息传递模型。顾名思义，消费者可以订阅一个或多个channel，生产者向对应channel发送消息后，所有订阅者都能收到相关消息。</p></blockquote><blockquote><ul><li>SUBSCRIBE channel [channel] ：订阅一个或多个频道</li><li>PUBLISH channel msg ：向一个频道发送消息</li><li>PSUBSCRIBE pattern[pattern] ：订阅与pattern格式匹配的所有频道</li></ul></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.5/202205061722917.png" alt="image-20220506172219845" style="zoom:80%;"><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.6.17/image-20230623150840696.png" alt="image-20230623150840696" style="zoom:80%;"><blockquote><p>基于PubSub的消息队列有哪些优缺点？</p></blockquote><blockquote><p>优点：采用发布订阅模型，支持多生产、多消费</p></blockquote><p>缺点：</p><blockquote><ul><li>不支持数据持久化</li><li>无法避免消息丢失</li><li>消息堆积有上限，超出时数据丢失</li></ul></blockquote><h2 id="基于stream的消息队列" tabindex="-1">基于Stream的消息队列 <a class="header-anchor" href="#基于stream的消息队列" aria-label="Permalink to &quot;基于Stream的消息队列&quot;">​</a></h2><blockquote><p>Stream 是 Redis 5.0 引入的一种新数据类型，可以实现一个功能非常完善的消息队列。</p></blockquote><h3 id="基本语法" tabindex="-1">基本语法 <a class="header-anchor" href="#基本语法" aria-label="Permalink to &quot;基本语法&quot;">​</a></h3><h4 id="发送消息" tabindex="-1">发送消息 <a class="header-anchor" href="#发送消息" aria-label="Permalink to &quot;发送消息&quot;">​</a></h4><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.6.17/image-20230623151917352.png" alt="image-20230623151917352" style="zoom:80%;"><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.6.17/image-20230623151816231.png" alt="image-20230623151816231" style="zoom:80%;"><h4 id="读取消息" tabindex="-1">读取消息 <a class="header-anchor" href="#读取消息" aria-label="Permalink to &quot;读取消息&quot;">​</a></h4><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.6.17/image-20230623151949164.png" alt="image-20230623151949164" style="zoom:80%;"><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.6.17/image-20230623152003432.png" alt="image-20230623152003432" style="zoom:80%;"><h4 id="阻塞消息-读取最新消息" tabindex="-1">阻塞消息-读取最新消息 <a class="header-anchor" href="#阻塞消息-读取最新消息" aria-label="Permalink to &quot;阻塞消息-读取最新消息&quot;">​</a></h4><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.6.17/image-20230623152040824.png" alt="image-20230623152040824" style="zoom:80%;"><blockquote><p>在业务开发中，我们可以循环的调用XREAD阻塞方式来查询最新消息，从而实现持续监听队列的效果，伪代码如下：</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.6.17/image-20230623152104343.png" alt="image-20230623152104343" style="zoom:80%;"><blockquote><p>STREAM类型消息队列的XREAD命令特点：</p><p>消息可回溯、一个消息可以被多个消费者读取、可以阻塞读取、有消息漏读的风险</p></blockquote><h3 id="消费者组" tabindex="-1">消费者组 <a class="header-anchor" href="#消费者组" aria-label="Permalink to &quot;消费者组&quot;">​</a></h3><blockquote><p><strong>消费者组（Consumer Group）</strong>：将多个消费者划分到一个组中，监听同一个队列。</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.6.17/image-20230623151249598.png" alt="image-20230623151249598" style="zoom:80%;"><h4 id="创建消费者组" tabindex="-1">创建消费者组 <a class="header-anchor" href="#创建消费者组" aria-label="Permalink to &quot;创建消费者组&quot;">​</a></h4><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.6.17/image-20230623151332058.png" alt="image-20230623151332058" style="zoom:67%;"><h4 id="读取消息-1" tabindex="-1">读取消息 <a class="header-anchor" href="#读取消息-1" aria-label="Permalink to &quot;读取消息&quot;">​</a></h4><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.6.17/image-20230623151413459.png" alt="image-20230623151413459" style="zoom:67%;"><h4 id="基本思路" tabindex="-1">基本思路 <a class="header-anchor" href="#基本思路" aria-label="Permalink to &quot;基本思路&quot;">​</a></h4><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.6.17/image-20230623151448208.png" alt="image-20230623151448208" style="zoom:67%;"><p>STREAM类型消息队列的XREADGROUP命令特点：</p><blockquote><p>消息可回溯、可以多消费者争抢消息，加快消费速度、可以阻塞读取</p><p>没有消息漏读的风险、有消息确认机制，保证消息至少被消费一次</p></blockquote><h1 id="redis6新功能" tabindex="-1">Redis6新功能 <a class="header-anchor" href="#redis6新功能" aria-label="Permalink to &quot;Redis6新功能&quot;">​</a></h1><h2 id="acl" tabindex="-1">ACL <a class="header-anchor" href="#acl" aria-label="Permalink to &quot;ACL&quot;">​</a></h2><h3 id="功能简介" tabindex="-1">功能简介 <a class="header-anchor" href="#功能简介" aria-label="Permalink to &quot;功能简介&quot;">​</a></h3><blockquote><p>ACL是Access Control List（访问控制列表）的缩写，该功能允许根据可以执行的命令和可以访问的键来限制某些连接。在Redis 5版本之前，Redis 安全规则只有密码控制 还有通过rename 来调整高危命令比如 flushdb ， KEYS* ， shutdown 等。</p></blockquote><p>Redis 6 则提供ACL的功能对用户进行更细粒度的权限控制 ：</p><blockquote><ul><li><p>接入权限:用户名和密码</p></li><li><p>可以执行的命令</p></li><li><p>可以操作的 KEY</p></li></ul></blockquote><p>参考官网：<a href="https://redis.io/topics/acl" target="_blank" rel="noreferrer">https://redis.io/topics/acl</a></p><h3 id="基础命令-1" tabindex="-1">基础命令 <a class="header-anchor" href="#基础命令-1" aria-label="Permalink to &quot;基础命令&quot;">​</a></h3><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 展现用户权限列表</span></span>
<span class="line"><span style="color:#A6ACCD;">acl list</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301241951932.png" alt="image-20230124195117764" style="zoom:67%;"><p>查看添加权限指令类别</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">acl cat</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301241952378.png" alt="image-20230124195219155" style="zoom:50%;"><p>加参数类型名可以查看类型下具体命令</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">acl cat string</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301241953530.png" alt="image-20230124195301362" style="zoom:50%;"><p>查看当前用户</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">acl whoami</span></span></code></pre></div><h3 id="acl规则" tabindex="-1">ACL规则 <a class="header-anchor" href="#acl规则" aria-label="Permalink to &quot;ACL规则&quot;">​</a></h3><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301242009943.png" alt="image-20230124200940780" style="zoom:67%;"><h3 id="实战演练-3" tabindex="-1">实战演练 <a class="header-anchor" href="#实战演练-3" aria-label="Permalink to &quot;实战演练&quot;">​</a></h3><p>通过命令创建新用户默认权限</p><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">acl setuser user1</span></span>
<span class="line"><span style="color:#A6ACCD;">acl list</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301242011726.png" alt="image-20230124201151593" style="zoom:67%;"><blockquote><p>在上面的示例中，我根本没有指定任何规则。如果用户不存在，这将使用just created的默认属性来创建用户。如果用户已经存在，则上面的命令将不执行任何操作。</p></blockquote><p>设置有用户名、密码(就叫password)、ACL权限、并启用的用户</p><blockquote><p>key必须带有cached，只有get操作</p></blockquote><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">acl setuser user2 on </span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">password </span><span style="color:#89DDFF;">~</span><span style="color:#A6ACCD;">cached:</span><span style="color:#89DDFF;">*</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">+</span><span style="color:#A6ACCD;">get</span></span></code></pre></div><p>切换用户，验证权限</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301242015479.png" alt="image-20230124201511294" style="zoom:67%;"><h2 id="io多线程" tabindex="-1">IO多线程 <a class="header-anchor" href="#io多线程" aria-label="Permalink to &quot;IO多线程&quot;">​</a></h2><blockquote><p>Redis 6 加入多线程,但跟 Memcached 这种从 IO处理到数据访问多线程的实现模式有些差异。Redis6 的<strong>多线程部分只是用来处理网络数据的读写和协议解析，执行命令仍然是单线程</strong>。之所以这么设计是不想因为多线程而变得复杂，需要去控制 key、lua、事务，LPUSH/LPOP 等等的并发问题</p></blockquote><blockquote><p>另外，多线程IO默认也是不开启的，需要再配置文件中配置</p></blockquote><div class="language-ini"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">io-threads-do-reads  yes </span></span>
<span class="line"><span style="color:#A6ACCD;">io-threads 4</span></span></code></pre></div><h2 id="工具支持cluster" tabindex="-1">工具支持Cluster <a class="header-anchor" href="#工具支持cluster" aria-label="Permalink to &quot;工具支持Cluster&quot;">​</a></h2><blockquote><p>之前老版Redis想要搭集群需要单独安装ruby环境，Redis 5 将 redis-trib.rb 的功能集成到 redis-cli ，另外官方 redis-benchmark 工具开始支持 cluster 模式了，通过多线程的方式对多个分片进行压测。</p></blockquote><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">redis-benchmark --help</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301242033775.png" alt="image-20230124203355523" style="zoom:67%;"><h2 id="redis其他新功能" tabindex="-1">Redis其他新功能 <a class="header-anchor" href="#redis其他新功能" aria-label="Permalink to &quot;Redis其他新功能&quot;">​</a></h2><blockquote><p>RESP3新的 Redis 通信协议：<strong>优化服务端与客户端之间通信</strong></p></blockquote><blockquote><p>Client side caching客户端缓存：基于 RESP3 协议实现的客户端缓存功能。为了进一步提升缓存的性能，<strong>将客户端经常访问的数据cache到客户端。减少TCP网络交互</strong>。</p></blockquote><blockquote><p>Proxy集群代理模式：Proxy 功能，让 Cluster 拥有像单实例一样的接入方式，降低大家使用cluster的门槛。不过需要注意的是代理不改变 Cluster 的功能限制，不支持的命令还是不会支持，比如跨 slot 的多Key操作。</p></blockquote><blockquote><p>Modules API：Redis 6中模块API开发进展非常大，因为Redis Labs为了开发复杂的功能，从一开始就用上Redis模块。Redis可以变成一个框架，利用Modules来构建不同系统，而不需要从头开始写然后还要BSD许可。Redis一开始就是一个向编写各种系统开放的平台。</p></blockquote><h1 id="redis-lua" tabindex="-1">Redis+Lua <a class="header-anchor" href="#redis-lua" aria-label="Permalink to &quot;Redis+Lua&quot;">​</a></h1><blockquote><p>Redis提供了Lua脚本功能，在一个脚本中编写多条Redis命令，确保多条命令执行时的原子性。Lua是一种编程语言，它的基本语法大家可以参考网站：<a href="https://www.runoob.com/lua/lua-tutorial.html" target="_blank" rel="noreferrer">https://www.runoob.com/lua/lua-tutorial.html</a></p></blockquote><blockquote><p>Redis 事务的执行并不能完全保证原子性，<strong>那么如何将一批命令操作做到原子性操作呢</strong>？Redis 支持通过 Lua 脚本来实现一批命令原子性操作，执行脚本的常用命令为 EVAL。</p></blockquote><h2 id="基本语法-1" tabindex="-1">基本语法 <a class="header-anchor" href="#基本语法-1" aria-label="Permalink to &quot;基本语法&quot;">​</a></h2><h3 id="eval函数" tabindex="-1">eval函数 <a class="header-anchor" href="#eval函数" aria-label="Permalink to &quot;eval函数&quot;">​</a></h3><p>这里重点介绍Redis提供的调用函数，语法如下：</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#82AAFF;">eval</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">return redis.call(&#39;set&#39;,&#39;name&#39;,&#39;jack&#39;)</span><span style="color:#89DDFF;">&quot;</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.11/202206130918562.png" alt="image-20220613091812423" style="zoom:80%;"><p>例如，我们要执行 redis.call(&#39;set&#39;, &#39;name&#39;, &#39;jack&#39;) 这个脚本，语法如下：</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.11/202206111630333.png" alt="image-20220611163001248" style="zoom:80%;"><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.11/202206111631198.png" alt="image-20220611163152120" style="zoom:80%;"><h3 id="基础用法" tabindex="-1">基础用法 <a class="header-anchor" href="#基础用法" aria-label="Permalink to &quot;基础用法&quot;">​</a></h3><p>例如，我们要执行set name jack，则脚本是这样：</p><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">redis.</span><span style="color:#82AAFF;">call</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">set</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">,</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">name</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">,</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">jack</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">)</span></span></code></pre></div><p>例如，我们要先执行set name Rose，再执行get name，则脚本如下：</p><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">redis.</span><span style="color:#82AAFF;">call</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">set</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">,</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">name</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">,</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">Rose</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">)</span></span>
<span class="line"><span style="color:#89DDFF;">local</span><span style="color:#A6ACCD;"> name </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> redis.</span><span style="color:#82AAFF;">call</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">get</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">, </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">name</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">)</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#A6ACCD;"> name</span></span></code></pre></div><p>写好脚本以后，需要用Redis命令来调用脚本，调用脚本的常见命令如下：</p><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">eval script numkeys key [key </span><span style="color:#89DDFF;">...</span><span style="color:#A6ACCD;">] arg [arg </span><span style="color:#89DDFF;">...</span><span style="color:#A6ACCD;">]</span></span></code></pre></div><p>就是把上面写的lua脚本放在一行，用双引号包起来，后面带个0就行，很简单</p><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#82AAFF;">eval</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">redis.call(&#39;set&#39;,&#39;name&#39;,&#39;Rose&#39;) local name = redis.call(&#39;get&#39;, &#39;name&#39;) return name</span><span style="color:#89DDFF;">&quot; </span><span style="color:#F78C6C;">0</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.2.30/202301242257687.png" alt="image-20230124225729562" style="zoom:67%;"><h3 id="高级用法-⭐" tabindex="-1">高级用法 ⭐ <a class="header-anchor" href="#高级用法-⭐" aria-label="Permalink to &quot;高级用法 ⭐&quot;">​</a></h3><blockquote><p>如果脚本中的key、value不想写死，可以作为参数传递。key类型参数会放入KEYS数组，其它参数会放入ARGV数组，在脚本中可以从KEYS和ARGV数组获取这些参数：</p></blockquote><p>KEYS对应key，ARGV对应value</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.11/202206111633285.png" alt="image-20220611163333199" style="zoom:80%;"><blockquote><p>比如我们给指定的<code>key</code>设置指定值<code>value</code>，并且设置过期时间<code>60</code>秒，实现原子性操作，如果操作成功就返回 1，否则返回 0</p></blockquote><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">EVAL</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">if redis.call(&#39;setnx&#39;, KEYS[1], ARGV[1]) == 1 then return redis.call(&#39;expire&#39;, KEYS[1], ARGV[2]) else return 0 end</span><span style="color:#89DDFF;">&quot;</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">key1</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">hello</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">60</span></span></code></pre></div><blockquote><p>获取指定<code>key</code>的值，如果存在就删除<code>key</code>，实现原子性操作，如果操作成功就返回 1，否则返回 0</p></blockquote><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">EVAL</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">if redis.call(&#39;get&#39;, KEYS[1]) == ARGV[1] then return redis.call(&#39;del&#39;, KEYS[1]) else return 0 end</span><span style="color:#89DDFF;">&quot;</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">key1</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">hello</span></span></code></pre></div><h2 id="整合语法" tabindex="-1">整合语法 <a class="header-anchor" href="#整合语法" aria-label="Permalink to &quot;整合语法&quot;">​</a></h2><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.11/202206130931649.png" alt="image-20220613093109542" style="zoom:80%;"><h2 id="redis分布式锁示例" tabindex="-1">Redis分布式锁示例 <a class="header-anchor" href="#redis分布式锁示例" aria-label="Permalink to &quot;Redis分布式锁示例&quot;">​</a></h2><p>unlock.lua</p><div class="language-lua"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">-- 这里的 KEYS[1] 就是锁的key，这里的ARGV[1] 就是当前线程标示</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 获取锁中的标示，判断是否与当前线程标示一致</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 比较线程标示与锁中的标示是否一致</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">if</span><span style="color:#A6ACCD;">(redis.</span><span style="color:#82AAFF;">call</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">get</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">, KEYS[</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;">]) </span><span style="color:#89DDFF;">==</span><span style="color:#A6ACCD;">  ARGV[</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;">]) </span><span style="color:#89DDFF;font-style:italic;">then</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">-- 一致，则删除锁</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">-- 释放锁 del key</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#A6ACCD;"> redis.</span><span style="color:#82AAFF;">call</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">del</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">, KEYS[</span><span style="color:#F78C6C;">1</span><span style="color:#A6ACCD;">])</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">end</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">-- 不一致，则直接返回</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span></span></code></pre></div><h1 id="慢查询日志" tabindex="-1">慢查询日志 <a class="header-anchor" href="#慢查询日志" aria-label="Permalink to &quot;慢查询日志&quot;">​</a></h1><h2 id="基本配置" tabindex="-1">基本配置 <a class="header-anchor" href="#基本配置" aria-label="Permalink to &quot;基本配置&quot;">​</a></h2><blockquote><p>Redis慢速日志是一个系统，用于记录超过指定值的查询执行时间。执行时间不包括I/O操作。比如与客户交谈、发送回复等等，而只是实际执行命令所需的时间（这是命令执行阶段，线程被阻塞且无法服务同时的其他请求）。</p></blockquote><blockquote><p>您可以使用两个参数配置慢日志：一个告诉Redis要超过多少执行时间（以微秒为单位）命令，另一个参数是慢日志。记录新命令时，最旧的命令将从记录的命令队列。</p></blockquote><p>关于慢查询日志的配置有两个：slowlog-max-len、slowlog-log-slower-than</p><h3 id="slowlog-max-len" tabindex="-1">slowlog-max-len <a class="header-anchor" href="#slowlog-max-len" aria-label="Permalink to &quot;slowlog-max-len&quot;">​</a></h3><blockquote><p><strong>作用</strong>：慢查询日志最多存储多少条。</p><p><strong>默认值</strong>：128。从源码中我们可以找到这个选项的默认值为 128</p><p><strong>配置建议</strong>：线上要调大慢查询列表，避免长命令在记录时被截断，不占大内存。线上配置 1000 以上</p></blockquote><h3 id="slowlog-log-slower-than" tabindex="-1">slowlog-log-slower-than <a class="header-anchor" href="#slowlog-log-slower-than" aria-label="Permalink to &quot;slowlog-log-slower-than&quot;">​</a></h3><blockquote><p><strong>作用</strong>：慢查询的预设阀值，当命令的耗时超过这个配置就会被记录到慢查询日志中。</p><p><strong>默认值：</strong> 10 ms。</p><p><strong>配置建议</strong>：默认值超过 10ms 被判定为慢查询，需要根据并发量来调整，高流量场景 配置为 1 ms。</p><p><strong>持久化日志</strong>：将慢查询日志持久化到数据库，避免丢失日志，可以开发可视化的查询界面供运维排查</p></blockquote><blockquote><p><strong>慢查询</strong>：<strong>在Redis执行时耗时超过某个阈值的命令，称为慢查询</strong>。</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.5/202205022151007.png" alt="image-20220502215135968" style="zoom:67%;"><p>慢查询的阈值可以通过配置指定：</p><blockquote><p>slowlog-log-slower-than：慢查询阈值，单位是微秒。默认是10000，建议1000</p></blockquote><blockquote><p>慢查询会被放入慢查询日志中，日志的长度有上限，可以通过配置指定：</p></blockquote><blockquote><p>slowlog-max-len：慢查询日志（本质是一个队列）的长度。默认是128，建议1000</p></blockquote><div class="language-apl"><button title="Copy Code" class="copy"></button><span class="lang">apl</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">config get slowlog</span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">log</span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">slower</span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">than</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 以下时间以微秒表示，因此</span><span style="color:#F78C6C;">1000000</span><span style="color:#A6ACCD;">相当于至一秒。请注意，负数将禁用慢速日志，而值为零强制记录每个命令</span></span>
<span class="line"><span style="color:#A6ACCD;">config set slowlog</span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">log</span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">slower</span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">than </span><span style="color:#F78C6C;">1000</span></span>
<span class="line"><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;"> 此长度没有限制。只需注意它会消耗内存。您可以使用SLOWLOG RESET回收慢速日志使用的内存</span></span>
<span class="line"><span style="color:#A6ACCD;">config set slowlog</span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">max</span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">len </span><span style="color:#F78C6C;">1000</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.5/202205022152955.png" alt="image-20220502215213920" style="zoom:80%;"><blockquote><p>修改这两个配置可以使用：config set命令：</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.5/202205022152690.png" alt="image-20220502215243655" style="zoom:80%;"><h2 id="基础使用" tabindex="-1">基础使用 <a class="header-anchor" href="#基础使用" aria-label="Permalink to &quot;基础使用&quot;">​</a></h2><blockquote><p>查看慢查询日志列表：</p></blockquote><blockquote><ul><li><strong>slowlog len：查询慢查询日志长度</strong></li><li><strong>slowlog get [n]：读取n条慢查询日志</strong></li><li><strong>slowlog reset：清空慢查询列表</strong></li></ul></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.5/202205022153581.png" alt="image-20220502215322544" style="zoom:80%;"><blockquote><p>通过RESP查看，很详细</p></blockquote><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.6.30/202207181053872.png" alt="image-20220718105333764" style="zoom:80%;"></div></div></main><footer class="VPDocFooter" data-v-6b87e69f data-v-37656e44><!--[--><!--]--><!----><nav class="prev-next" data-v-37656e44><div class="pager" data-v-37656e44><a class="pager-link prev" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/MySQL/%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8.html" data-v-37656e44><span class="desc" data-v-37656e44>Previous page</span><span class="title" data-v-37656e44>分库分表</span></a></div><div class="pager" data-v-37656e44><a class="pager-link next" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/Redis/Redis%E4%BC%98%E5%8C%96.html" data-v-37656e44><span class="desc" data-v-37656e44>Next page</span><span class="title" data-v-37656e44>Redis优化</span></a></div></nav></footer><!--[--><!--]--></div></div></div><!--[--><!--]--></div></div><!----><!--[--><!--]--></div></div>
    <script>window.__VP_HASH_MAP__=JSON.parse("{\"2、数据库_mysql_mysql面试_基础.md\":\"40da680a\",\"1、学前端_5、小程序_小程序项目.md\":\"60a1629b\",\"1、学前端_4、node_知识篇.md\":\"a7fb500e\",\"1、学前端_2、js_ts_es6 进阶.md\":\"6d07ba10\",\"1、学前端_3、vue_vue3_vue3进阶.md\":\"7ac622b4\",\"5、运维_jenkins.md\":\"929081f8\",\"1、学前端_2、js_ts_typescript.md\":\"875a4aa4\",\"2、数据库_mysql_mysql核心_设计.md\":\"7faf46d1\",\"2、数据库_mysql_mysql核心_基础.md\":\"d8e97f3e\",\"1、学前端_1、html_css_html基础.md\":\"7584d076\",\"1、学前端_5、专题篇_问题篇.md\":\"e893aaa2\",\"2、数据库_mysql_mysql面试_进阶.md\":\"f934806d\",\"3、springboot_运维_原理.md\":\"f4a39db6\",\"2、数据库_influxdb.md\":\"6e1711e1\",\"3、springboot_新特性.md\":\"cdf3e307\",\"mybatis_mybatisplus_jpa.md\":\"8e41681b\",\"1、学前端_5、小程序_小程序优化.md\":\"a2185198\",\"2、数据库_redis_redis基础.md\":\"856df0e0\",\"linux_实用脚本.md\":\"f2299dd5\",\"4、微服务_必备_分布式基础.md\":\"d49863d5\",\"2、数据库_redis_redis优化.md\":\"e66ae32f\",\"4、微服务_springsecurity_进阶篇.md\":\"235a8e9e\",\"5、运维_chatgpt.md\":\"10db3823\",\"2、数据库_mysql_分库分表.md\":\"e1c8a095\",\"start.md\":\"9bc1ff8d\",\"5、运维_github.md\":\"2ec6c735\",\"java学前端_css.md\":\"f11b47f0\",\"1、学前端_5、专题篇_知识篇.md\":\"a463ed8d\",\"linux_软件部署.md\":\"d6722925\",\"2、数据库_neo4j.md\":\"97ad22ac\",\"team.md\":\"ce467a6a\",\"nginx_实战篇.md\":\"7785486e\",\"index.md\":\"8c3ec167\",\"计算机基础_计算机网络_网络基础.md\":\"7a54a85d\",\"1、学前端_4、node_进阶篇.md\":\"60f6db69\",\"java_java集合.md\":\"a049b313\",\"1、学前端_3、vue_vue3_vue3高级.md\":\"614d1516\",\"1、学前端_5、小程序_微信小程序.md\":\"9a4be771\",\"5、运维_netty.md\":\"12ca0278\",\"2、数据库_mysql_mysql核心_运维.md\":\"83f97c16\",\"idea_vs code.md\":\"afdcb593\",\"java学前端_vue3_组件.md\":\"1086884e\",\"idea_chrome.md\":\"4a32afbc\",\"云原生_k8s.md\":\"db58e65a\",\"2、数据库_mysql_mysql核心_进阶.md\":\"61d16dff\",\"ssm_springbatch.md\":\"f799ab4a\",\"三高_分布式.md\":\"db1b8a1b\",\"2、数据库_elasticsearch_1、es基础.md\":\"04d17448\",\"linux_linux基础.md\":\"4b0bf394\",\"idea_idea插件.md\":\"fa86e45a\",\"可视化 _ 监控_可视化大屏.md\":\"004553bd\",\"2、数据库_mongodb_整合.md\":\"3c47d7f4\",\"4、微服务_springsecurity_基础篇.md\":\"534a3401\",\"4、微服务_进阶.md\":\"69095c58\",\"计算机基础_计算机基础_操作系统.md\":\"0f75d113\",\"可视化 _ 监控_zabbix.md\":\"71f2270e\",\"nginx_基础篇.md\":\"c7d8bb50\",\"1、学前端_4、node_项目实战.md\":\"bc5065b8\",\"2、数据库_redis_redis原理.md\":\"5cedf685\",\"可视化 _ 监控_监控基础.md\":\"ac56ce4d\",\"三高_高并发.md\":\"ea9ffc99\",\"2、数据库_redis_redis高级.md\":\"1d5872f6\",\"1、学前端_4、node_基础篇.md\":\"581cc13a\",\"2、数据库_mongodb_基础.md\":\"fb7a0a29\",\"idea_idea基础.md\":\"6f2f9638\",\"4、微服务_必备_sentinel.md\":\"2edfbf6c\",\"2、数据库_elasticsearch_3、es高级.md\":\"ef146606\",\"1、学前端_3、vue_vue3_vue3新语法.md\":\"8afd5409\",\"消息中间件_canal.md\":\"3949163c\",\"ssm_maven.md\":\"2c5e12ed\",\"4、微服务_springsecurity_高级篇.md\":\"882d3ff3\",\"linux_linux进阶.md\":\"188ef7b4\",\"计算机基础_设计模式_uml.md\":\"634ba256\",\"计算机基础_算法_leetcode.md\":\"77162fb9\",\"项目实战_小兔鲜_进阶篇1.md\":\"17c52c81\",\"1、学前端_2、js_ts_es6 基础.md\":\"fda3f18b\",\"项目实战_小兔鲜_进阶篇2.md\":\"a0f23006\",\"软件测试_测试基础.md\":\"8c1060cd\",\"2、数据库_redis_本地缓存.md\":\"00617fe6\",\"nginx_面试篇.md\":\"e3fb373a\",\"mybatis_mybatisplus_mybatis.md\":\"9239e0ad\",\"linux_shell.md\":\"ae53d83b\",\"2、数据库_mysql_mysql核心_优化.md\":\"36230425\",\"项目实战_项目推荐.md\":\"f9d97630\",\"mybatis_mybatisplus_mybatisplus.md\":\"0030fd35\",\"项目实战_百度地图_进阶篇.md\":\"c8b93267\",\"三高_高可用.md\":\"323840c5\",\"java_java新特性.md\":\"22abf56d\",\"软件测试_压力测试.md\":\"9ab44440\",\"java学前端_html_js.md\":\"e0fcd240\",\"2、数据库_redis_redis实战.md\":\"d6daeeab\",\"nginx_进阶篇.md\":\"e6b63195\",\"三高_秒杀.md\":\"3878bb64\",\"5、运维_git.md\":\"0264925c\",\"java_java进阶.md\":\"e79cb5b4\",\"并发 _ 多线程_基础篇.md\":\"7adbfac5\",\"项目实战_百度地图_基础篇.md\":\"8afa5954\",\"java学前端_react.md\":\"3ec827dd\",\"1、学前端_1、html_css_css基础.md\":\"01b56712\",\"项目实战_小兔鲜_基础篇.md\":\"646f5df5\",\"1、学前端_2、js_ts_js 基础.md\":\"cb13e36f\",\"可视化 _ 监控_监控进阶.md\":\"0cdbc292\",\"计算机基础_设计模式_基础篇.md\":\"51617287\",\"计算机基础_数据结构_基础篇.md\":\"b2bfd8d4\",\"项目实战_苍穹外卖_进阶篇.md\":\"48415e41\",\"ssm_spring.md\":\"ab514659\",\"消息中间件_rabbitmq.md\":\"45b1eb28\",\"1、学前端_1、html_css_网页进阶.md\":\"db998248\",\"消息中间件_kafka.md\":\"b747dabf\",\"云原生_docker.md\":\"983c7ba7\",\"4、微服务_必备_分布式锁.md\":\"5af1cf8d\",\"消息中间件_rocketmq.md\":\"d441da85\",\"项目实战_黑马头条_基础篇.md\":\"b05af3a6\",\"ssm_springmvc.md\":\"81b9714f\",\"项目实战_支付.md\":\"1d7407dd\",\"项目实战_黑马头条_进阶篇2.md\":\"bff0015b\",\"项目实战_黑马头条_进阶篇.md\":\"19f18388\",\"java学前端_vue2_组件.md\":\"58c6b1df\",\"3、springboot_基础篇.md\":\"529c66f4\",\"3、springboot_应用篇.md\":\"8b92aa61\",\"项目实战_黑马头条_高级篇.md\":\"227c08c1\",\"1、学前端_5、小程序_uniapp.md\":\"71a282b4\",\"项目实战_云尚办公_基础篇.md\":\"1fe188ba\",\"并发 _ 多线程_并发完善.md\":\"26619c46\",\"1、学前端_2、js_ts_js 进阶.md\":\"657dfb8f\",\"java_java高级.md\":\"23782d1a\",\"java_java基础.md\":\"86d67c77\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"en-US\",\"dir\":\"ltr\",\"title\":\"VitePress\",\"description\":\"A VitePress site\",\"base\":\"/notebook/\",\"head\":[],\"appearance\":true,\"themeConfig\":{\"algolia\":{\"appId\":\"DW7O63I9IR\",\"apiKey\":\"f8ed758cdb288a8b06542bc35923c1a1\",\"indexName\":\"notebook\"},\"sidebar\":[{\"text\":\"Java\",\"collapsed\":true,\"items\":[{\"text\":\"Java基础\",\"link\":\"/Java/Java基础\"},{\"text\":\"Java新特性\",\"link\":\"/Java/Java新特性\"},{\"text\":\"Java进阶\",\"link\":\"/Java/Java进阶\"},{\"text\":\"Java集合\",\"link\":\"/Java/Java集合\"},{\"text\":\"Java高级\",\"link\":\"/Java/Java高级\"}]},{\"text\":\"Linux\",\"collapsed\":true,\"items\":[{\"text\":\"Linux基础\",\"link\":\"/Linux/Linux基础\"},{\"text\":\"Linux新特性\",\"link\":\"/Linux/Linux进阶\"},{\"text\":\"Shell脚本\",\"link\":\"/Linux/Shell\"},{\"text\":\"实用脚本\",\"link\":\"/Linux/实用脚本\"},{\"text\":\"软件部署\",\"link\":\"/Linux/软件部署\"}]},{\"text\":\"Nginx\",\"collapsed\":true,\"items\":[{\"text\":\"基础篇\",\"link\":\"/Nginx/基础篇\"},{\"text\":\"进阶篇\",\"link\":\"/Nginx/进阶篇\"},{\"text\":\"实战篇\",\"link\":\"/Nginx/实战篇\"},{\"text\":\"面试篇\",\"link\":\"/Nginx/面试篇\"}]},{\"text\":\"SSM\",\"collapsed\":true,\"items\":[{\"text\":\"Maven\",\"link\":\"/SSM/Maven\"},{\"text\":\"Spring\",\"link\":\"/SSM/Spring\"},{\"text\":\"SpringMVC\",\"link\":\"/SSM/SpringMVC\"},{\"text\":\"SpringBatch\",\"link\":\"/SSM/SpringBatch\"}]},{\"text\":\"SpringBoot\",\"collapsed\":true,\"items\":[{\"text\":\"基础篇\",\"link\":\"/3、SpringBoot/基础篇\"},{\"text\":\"应用篇\",\"link\":\"/3、SpringBoot/应用篇\"},{\"text\":\"新特性\",\"link\":\"/3、SpringBoot/新特性\"},{\"text\":\"运维&原理\",\"link\":\"/3、SpringBoot/运维&原理\"}]},{\"text\":\"SpringCloud\",\"collapsed\":true,\"items\":[{\"text\":\"SpringCloud\",\"link\":\"/4、微服务/进阶\"},{\"text\":\"Sentinel\",\"link\":\"/4、微服务/必备/Sentinel\"}]},{\"text\":\"SpringSecurity\",\"collapsed\":true,\"items\":[{\"text\":\"SpringSecurity基础篇\",\"link\":\"/4、微服务/SpringSecurity/基础篇\"},{\"text\":\"SpringSecurity进阶篇\",\"link\":\"/4、微服务/SpringSecurity/进阶篇\"},{\"text\":\"SpringSecurity高级篇\",\"link\":\"/4、微服务/SpringSecurity/高级篇\"}]},{\"text\":\"Mybatis & MybatisPlus\",\"collapsed\":true,\"items\":[{\"text\":\"Mybatis\",\"link\":\"/Mybatis&MybatisPlus/Mybatis\"},{\"text\":\"MybatisPlus\",\"link\":\"/Mybatis&MybatisPlus/MybatisPlus\"},{\"text\":\"JPA\",\"link\":\"/Mybatis&MybatisPlus/JPA\"}]},{\"text\":\"Git & ChatGPT\",\"collapsed\":true,\"items\":[{\"text\":\"Git\",\"link\":\"/5、运维/Git\"},{\"text\":\"Github\",\"link\":\"/5、运维/Github\"},{\"text\":\"ChatGPT\",\"link\":\"/5、运维/ChatGPT\"},{\"text\":\"Jenkins\",\"link\":\"/5、运维/Jenkins\"},{\"text\":\"Netty\",\"link\":\"/5、运维/Netty\"}]},{\"text\":\"数据库\",\"collapsed\":true,\"items\":[{\"text\":\"MySQL\",\"collapsed\":true,\"items\":[{\"text\":\"MySQL基础\",\"link\":\"/2、数据库/MySQL/MySQL核心/基础\"},{\"text\":\"MySQL进阶\",\"link\":\"/2、数据库/MySQL/MySQL核心/进阶\"},{\"text\":\"MySQL优化\",\"link\":\"/2、数据库/MySQL/MySQL核心/优化\"},{\"text\":\"MySQL设计\",\"link\":\"/2、数据库/MySQL/MySQL核心/设计\"},{\"text\":\"MySQL运维\",\"link\":\"/2、数据库/MySQL/MySQL核心/运维\"},{\"text\":\"分库分表\",\"link\":\"/2、数据库/MySQL/分库分表\"}]},{\"text\":\"Redis\",\"collapsed\":true,\"items\":[{\"text\":\"Redis基础\",\"link\":\"/2、数据库/Redis/Redis基础\"},{\"text\":\"Redis优化\",\"link\":\"/2、数据库/Redis/Redis优化\"},{\"text\":\"Redis原理\",\"link\":\"/2、数据库/Redis/Redis原理\"},{\"text\":\"Redis高级\",\"link\":\"/2、数据库/Redis/Redis高级\"},{\"text\":\"Redis实战\",\"link\":\"/2、数据库/Redis/Redis实战\"},{\"text\":\"本地缓存\",\"link\":\"/2、数据库/Redis/本地缓存\"}]},{\"text\":\"MongoDB\",\"collapsed\":true,\"items\":[{\"text\":\"MongoDB基础\",\"link\":\"/2、数据库/MongoDB/基础\"},{\"text\":\"MongoDB进阶\",\"link\":\"/2、数据库/MongoDB/整合\"}]},{\"text\":\"ElasticSearch\",\"collapsed\":true,\"items\":[{\"text\":\"ES基础\",\"link\":\"/2、数据库/ElasticSearch/1、ES基础\"},{\"text\":\"ES高级\",\"link\":\"/2、数据库/ElasticSearch/3、ES高级\"}]},{\"text\":\"InfluxDB\",\"link\":\"/2、数据库/influxdb\"},{\"text\":\"Neo4j\",\"link\":\"/2、数据库/Neo4j\"}]},{\"text\":\"高并发 & 秒杀 & 分布式\",\"collapsed\":true,\"items\":[{\"text\":\"分布式理论\",\"link\":\"/三高/分布式\"},{\"text\":\"分布式锁\",\"link\":\"/4、微服务/必备/分布式锁\"},{\"text\":\"秒杀\",\"link\":\"/三高/秒杀\"},{\"text\":\"高可用\",\"link\":\"/三高/高可用\"},{\"text\":\"高并发\",\"link\":\"/三高/高并发\"}]},{\"text\":\"云原生\",\"collapsed\":true,\"items\":[{\"text\":\"Docker\",\"link\":\"/云原生/Docker\"},{\"text\":\"K8S\",\"link\":\"/云原生/K8S\"}]},{\"text\":\"可视化 & 监控\",\"collapsed\":true,\"items\":[{\"text\":\"监控基础\",\"link\":\"/可视化 & 监控/监控基础\"},{\"text\":\"监控进阶\",\"link\":\"/可视化 & 监控/监控进阶\"},{\"text\":\"可视化大屏\",\"link\":\"/可视化 & 监控/可视化大屏\"},{\"text\":\"Zabbix\",\"link\":\"/可视化 & 监控/Zabbix\"}]},{\"text\":\"学前端\",\"collapsed\":true,\"items\":[{\"text\":\"HTML+CSS\",\"collapsed\":true,\"items\":[{\"text\":\"HTML基础\",\"link\":\"/1、学前端/1、HTML+CSS/HTML基础\"},{\"text\":\"CSS基础\",\"link\":\"/1、学前端/1、HTML+CSS/CSS基础\"},{\"text\":\"网页进阶\",\"link\":\"/1、学前端/1、HTML+CSS/网页进阶\"}]},{\"text\":\"JS+TS\",\"collapsed\":true,\"items\":[{\"text\":\"JS基础\",\"link\":\"/1、学前端/2、JS+TS/JS 基础\"},{\"text\":\"JS进阶\",\"link\":\"/1、学前端/2、JS+TS/JS 进阶\"},{\"text\":\"ES6基础\",\"link\":\"/1、学前端/2、JS+TS/ES6 基础\"},{\"text\":\"ES6进阶\",\"link\":\"/1、学前端/2、JS+TS/ES6 进阶\"},{\"text\":\"TS基础\",\"link\":\"/1、学前端/2、JS+TS/TypeScript\"}]},{\"text\":\"NodeJS\",\"collapsed\":true,\"items\":[{\"text\":\"Node基础\",\"link\":\"/1、学前端/4、Node/基础篇\"},{\"text\":\"Node进阶\",\"link\":\"/1、学前端/4、Node/进阶篇\"},{\"text\":\"项目实战\",\"link\":\"/1、学前端/4、Node/项目实战\"}]},{\"text\":\"Vue\",\"collapsed\":true,\"items\":[{\"text\":\"Vue3进阶\",\"link\":\"/1、学前端/3、Vue/Vue3/Vue3进阶\"},{\"text\":\"Vue3高级\",\"link\":\"/1、学前端/3、Vue/Vue3/Vue3高级\"},{\"text\":\"Vue3新语法\",\"link\":\"/1、学前端/3、Vue/Vue3/Vue3新语法\"},{\"text\":\"项目实战\",\"link\":\"/1、学前端/3、Vue/Vue2/Vue2项目\"}]},{\"text\":\"小程序\",\"collapsed\":true,\"items\":[{\"text\":\"小程序基础\",\"link\":\"/1、学前端/5、小程序/微信小程序\"},{\"text\":\"小程序优化\",\"link\":\"/1、学前端/5、小程序/小程序优化\"},{\"text\":\"uniapp\",\"link\":\"/1、学前端/5、小程序/uniapp\"},{\"text\":\"项目实战\",\"link\":\"/1、学前端/5、小程序/小程序项目\"}]}]},{\"text\":\"计算机基础\",\"collapsed\":true,\"items\":[{\"text\":\"数据结构\",\"link\":\"/计算机基础/数据结构/基础篇\"},{\"text\":\"操作系统\",\"link\":\"/计算机基础/计算机基础/操作系统\"},{\"text\":\"设计模式\",\"link\":\"/计算机基础/设计模式/基础篇\"},{\"text\":\"计算机网络\",\"link\":\"/计算机基础/计算机网络/网络基础\"},{\"text\":\"UML\",\"link\":\"/计算机基础/设计模式/UML\"},{\"text\":\"LeetCode\",\"link\":\"/计算机基础/算法/LeetCode\"}]},{\"text\":\"项目实战\",\"collapsed\":true,\"items\":[{\"text\":\"云尚办公\",\"collapsed\":true,\"items\":[{\"text\":\"基础篇\",\"link\":\"/项目实战/云尚办公/基础篇\"}]},{\"text\":\"小兔鲜\",\"collapsed\":true,\"items\":[{\"text\":\"基础篇\",\"link\":\"/项目实战/小兔鲜/基础篇\"},{\"text\":\"进阶篇1\",\"link\":\"/项目实战/小兔鲜/进阶篇1\"},{\"text\":\"进阶篇2\",\"link\":\"/项目实战/小兔鲜/进阶篇2\"}]},{\"text\":\"地图\",\"collapsed\":true,\"items\":[{\"text\":\"基础篇\",\"link\":\"/项目实战/百度地图/基础篇\"},{\"text\":\"进阶篇\",\"link\":\"/项目实战/百度地图/进阶篇\"}]},{\"text\":\"苍穹外卖\",\"collapsed\":true,\"items\":[{\"text\":\"进阶篇\",\"link\":\"/项目实战/苍穹外卖/进阶篇\"}]},{\"text\":\"黑马头条\",\"collapsed\":true,\"items\":[{\"text\":\"基础篇\",\"link\":\"/项目实战/黑马头条/基础篇\"},{\"text\":\"进阶篇\",\"link\":\"/项目实战/黑马头条/进阶篇\"},{\"text\":\"进阶篇2\",\"link\":\"/项目实战/黑马头条/进阶篇2\"},{\"text\":\"高级篇\",\"link\":\"/项目实战/黑马头条/高级篇\"}]},{\"text\":\"支付\",\"link\":\"/项目实战/支付\"},{\"text\":\"项目推荐\",\"link\":\"/项目实战/项目推荐\"}]},{\"text\":\"团队成员\",\"link\":\"/team\"}],\"siteTitle\":\"任硕的文档\",\"logo\":\"/Vue.png\",\"nav\":[{\"text\":\"Java学前端\",\"items\":[{\"items\":[{\"text\":\"HTML+JS\",\"link\":\"/Java学前端/HTML+JS\"},{\"text\":\"CSS\",\"link\":\"/Java学前端/CSS\"},{\"text\":\"Vue2+组件\",\"link\":\"/Java学前端/Vue2+组件\"},{\"text\":\"Vue3+组件\",\"link\":\"/Java学前端/Vue3+组件\"},{\"text\":\"React\",\"link\":\"/Java学前端/React\"}]}],\"activeMatch\":\"/Java/\"},{\"text\":\"软件测试\",\"items\":[{\"items\":[{\"text\":\"测试基础\",\"link\":\"/软件测试/测试基础\"},{\"text\":\"压力测试\",\"link\":\"/软件测试/压力测试\"}]}]},{\"text\":\"多线程\",\"items\":[{\"items\":[{\"text\":\"基础篇\",\"link\":\"/并发 & 多线程/基础篇\"},{\"text\":\"进阶篇\",\"link\":\"/并发 & 多线程/并发完善\"}]}]},{\"text\":\"开发工具\",\"items\":[{\"items\":[{\"text\":\"Chrome\",\"link\":\"/IDEA/Chrome\"},{\"text\":\"IDEA基础\",\"link\":\"/IDEA/IDEA基础\"},{\"text\":\"IDEA插件\",\"link\":\"/IDEA/IDEA插件\"},{\"text\":\"VS Code\",\"link\":\"/IDEA/VS Code\"}]}]},{\"text\":\"消息中间件\",\"items\":[{\"items\":[{\"text\":\"RabbitMQ\",\"link\":\"/消息中间件/RabbitMQ\"},{\"text\":\"RocketMQ\",\"link\":\"/消息中间件/RocketMQ\"},{\"text\":\"Kafka\",\"link\":\"/消息中间件/Kafka\"},{\"text\":\"Canal\",\"link\":\"/消息中间件/Canal\"}]}]}],\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://github.com/renshuo123/renshuo123.github.io\"},{\"icon\":\"twitter\",\"link\":\"#\"},{\"icon\":{\"svg\":\"<svg t=\\\"1676028692954\\\" class=\\\"icon\\\" ...</path></svg>\"},\"link\":\"https://github.com/\"}]},\"locales\":{},\"scrollOffset\":90,\"cleanUrls\":false}");</script>
    
  </body>
</html>